diff --git a/css/base.css b/css/base.css new file mode 100644 index 0000000..a11e818 --- /dev/null +++ b/css/base.css @@ -0,0 +1,419 @@ +/* + * Base CSS + */ + + +/* + * Contents + * + * Body resets + * Custom type + * Messages + * Container + * Masthead + * Posts and pages + * Pagination + * Reverse layout + * Themes + */ + + +/* + * Body resets + * + * Update the foundational and global aspects of the page. + */ + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +html, +body { + margin: 0; + padding: 0; +} + +html { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 1.5; +} +@media (min-width: 38em) { + html { + font-size: 20px; + } +} + +body { + color: #515151; + background-color: #fff; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +/* No `:visited` state is required by default (browsers will use `a`) */ +a { + color: #268bd2; + text-decoration: none; +} +a strong { + color: inherit; +} +/* `:focus` is linked to `:hover` for basic accessibility */ +a:hover, +a:focus { + text-decoration: underline; +} + +/* Headings */ +h1, h2, h3, h4, h5, h6 { + margin-bottom: .5rem; + font-weight: bold; + line-height: 1.25; + color: #313131; + text-rendering: optimizeLegibility; +} +h1 { + font-size: 2rem; +} +h2 { + margin-top: 1rem; + font-size: 1.5rem; +} +h3 { + margin-top: 1.5rem; + font-size: 1.25rem; +} +h4, h5, h6 { + margin-top: 1rem; + font-size: 1rem; +} + +/* Body text */ +p { + margin-top: 0; + margin-bottom: 1rem; +} + +strong { + color: #303030; +} + + +/* Lists */ +ul, ol, dl { + margin-top: 0; + margin-bottom: 1rem; +} + +dt { + font-weight: bold; +} +dd { + margin-bottom: .5rem; +} + +/* Misc */ +hr { + position: relative; + margin: 1.5rem 0; + border: 0; + border-top: 1px solid #eee; + border-bottom: 1px solid #fff; +} + +abbr { + font-size: 85%; + font-weight: bold; + color: #555; + text-transform: uppercase; +} +abbr[title] { + cursor: help; + border-bottom: 1px dotted #e5e5e5; +} + +/* Code */ +code, +pre { + font-family: Menlo, Monaco, "Courier New", monospace; +} +code { + padding: .25em .5em; + font-size: 85%; + color: #bf616a; + background-color: #f9f9f9; + border-radius: 3px; +} +pre { + display: block; + margin-top: 0; + margin-bottom: 1rem; + padding: 1rem; + font-size: .8rem; + line-height: 1.4; + white-space: pre; + white-space: pre-wrap; + word-break: break-all; + word-wrap: break-word; + background-color: #f9f9f9; +} +pre code { + padding: 0; + font-size: 100%; + color: inherit; + background-color: transparent; +} + +/* Pygments via Jekyll */ +.highlight { + margin-bottom: 1rem; + border-radius: 4px; +} +.highlight pre { + margin-bottom: 0; +} + +/* Gist via GitHub Pages */ +.gist .gist-file { + font-family: Menlo, Monaco, "Courier New", monospace !important; +} +.gist .markdown-body { + padding: 15px; +} +.gist pre { + padding: 0; + background-color: transparent; +} +.gist .gist-file .gist-data { + font-size: .8rem !important; + line-height: 1.4; +} +.gist code { + padding: 0; + color: inherit; + background-color: transparent; + border-radius: 0; +} + +/* Quotes */ +blockquote { + padding: .5rem 1rem; + margin: .8rem 0; + color: #7a7a7a; + border-left: .25rem solid #e5e5e5; +} +blockquote p:last-child { + margin-bottom: 0; +} +@media (min-width: 30em) { + blockquote { + padding-right: 5rem; + padding-left: 1.25rem; + } +} + +img { + display: block; + max-width: 100%; + margin: 0 0 1rem; + border-radius: 5px; +} + +/* Tables */ +table { + margin-bottom: 1rem; + width: 100%; + border: 1px solid #e5e5e5; + border-collapse: collapse; +} +td, +th { + padding: .25rem .5rem; + border: 1px solid #e5e5e5; +} +tbody tr:nth-child(odd) td, +tbody tr:nth-child(odd) th { + background-color: #f9f9f9; +} + + +/* + * Custom type + * + * Extend paragraphs with `.lead` for larger introductory text. + */ + +.lead { + font-size: 1.25rem; + font-weight: 300; +} + + +/* + * Messages + * + * Show alert messages to users. You may add it to single elements like a `
`, + * or to a parent if there are multiple elements to show. + */ + +.message { + margin-bottom: 1rem; + padding: 1rem; + color: #717171; + background-color: #f9f9f9; +} + + +/* + * Container + * + * Center the page content. + */ + +.container { + max-width: 38rem; + padding-left: 1rem; + padding-right: 1rem; + margin-left: auto; + margin-right: auto; +} + + +/* + * Masthead + * + * Super small header above the content for site name and short description. + */ + +.masthead { + padding-top: 1rem; + padding-bottom: 1rem; + margin-bottom: 3rem; +} +.masthead-title { + margin-top: 0; + margin-bottom: 0; + color: #505050; +} +.masthead-title a { + color: #505050; +} +.masthead-title small { + font-size: 75%; + font-weight: 400; + color: #c0c0c0; + letter-spacing: 0; +} + + +/* + * Posts and pages + * + * Each post is wrapped in `.post` and is used on default and post layouts. Each + * page is wrapped in `.page` and is only used on the page layout. + */ + +.page, +.post { + margin-bottom: 4em; +} + +/* Blog post or page title */ +.page-title, +.post-title, +.post-title a { + color: #303030; +} +.page-title, +.post-title { + margin-top: 0; +} + +/* Meta data line below post title */ +.post-date { + display: block; + margin-top: -.5rem; + margin-bottom: 1rem; + color: #9a9a9a; +} + +/* Related posts */ +.related { + padding-top: 2rem; + padding-bottom: 2rem; + border-top: 1px solid #eee; +} +.related-posts { + padding-left: 0; + list-style: none; +} +.related-posts h3 { + margin-top: 0; +} +.related-posts li small { + font-size: 75%; + color: #999; +} +.related-posts li a:hover { + color: #268bd2; + text-decoration: none; +} +.related-posts li a:hover small { + color: inherit; +} + + +/* + * Pagination + * + * Super lightweight (HTML-wise) blog pagination. `span`s are provide for when + * there are no more previous or next posts to show. + */ + +.pagination { + overflow: hidden; /* clearfix */ + margin-left: -1rem; + margin-right: -1rem; + font-family: "PT Sans", Helvetica, Arial, sans-serif; + color: #ccc; + text-align: center; +} + +/* Pagination items can be `span`s or `a`s */ +.pagination-item { + display: block; + padding: 1rem; + border: 1px solid #eee; +} +.pagination-item:first-child { + margin-bottom: -1px; +} + +/* Only provide a hover state for linked pagination items */ +a.pagination-item:hover { + background-color: #f5f5f5; +} + +@media (min-width: 30em) { + .pagination { + margin: 3rem 0; + } + .pagination-item { + float: left; + width: 50%; + } + .pagination-item:first-child { + margin-bottom: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + } + .pagination-item:last-child { + margin-left: -1px; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + } +} diff --git a/css/jfdic.css b/css/jfdic.css new file mode 100644 index 0000000..47b4a24 --- /dev/null +++ b/css/jfdic.css @@ -0,0 +1,532 @@ +/* + * JFDIC theme + */ + +/* + * Global resets + * + * Update the foundational and global aspects of the page. + */ + +/* Prevent scroll on narrow devices */ +html, +body { + overflow-x: hidden; +} + +html { + font-family: "PT Serif", Georgia, "Times New Roman", serif; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "PT Sans", Helvetica, Arial, sans-serif; + font-weight: 400; + color: #313131; + letter-spacing: -.025rem; +} + + +/* + * Wrapper + * + * The wrapper is used to position site content when the sidebar is toggled. We + * use an outter wrap to position the sidebar without interferring with the + * regular page content. + */ + +.wrap { + position: relative; + width: 100%; +} + + +/* + * Container + * + * Center the page content. + */ + +.container { + max-width: 28rem; +} +@media (min-width: 38em) { + .container { + max-width: 32rem; + } +} +@media (min-width: 56em) { + .container { + max-width: 38rem; + } +} + + +/* + * Masthead + * + * Super small header above the content for site name and short description. + */ + +.masthead { + padding-top: 1rem; + padding-bottom: 1rem; + margin-bottom: 3rem; + border-bottom: 1px solid #eee; +} +.masthead-title { + margin-top: 0; + margin-bottom: 0; + color: #505050; +} +.masthead-title a { + color: #505050; +} +.masthead-title small { + font-size: 75%; + font-weight: 400; + color: #c0c0c0; + letter-spacing: 0; +} + +@media (max-width: 48em) { + .masthead-title { + text-align: center; + } + .masthead-title small { + display: none; + } +} + + +/* + * Sidebar + * + * The sidebar is the drawer, the item we are toggling with our handy hamburger + * button in the corner of the page. + * + * This particular sidebar implementation was inspired by Chris Coyier's + * "Offcanvas Menu with CSS Target" article, and the checkbox variation from the + * comments by a reader. It modifies both implementations to continue using the + * checkbox (no change in URL means no polluted browser history), but this uses + * `position` for the menu to avoid some potential content reflow issues. + * + * Source: http://css-tricks.com/off-canvas-menu-with-css-target/#comment-207504 + */ + +/* Style and "hide" the sidebar */ +.sidebar { + position: fixed; + top: 0; + bottom: 0; + left: -14rem; + width: 14rem; + visibility: hidden; + overflow-y: auto; + font-family: "PT Sans", Helvetica, Arial, sans-serif; + font-size: .875rem; /* 15px */ + color: rgba(255,255,255,.6); + background-color: #202020; + -webkit-transition: all .3s ease-in-out; + transition: all .3s ease-in-out; +} +@media (min-width: 30em) { + .sidebar { + font-size: .75rem; /* 14px */ + } +} + +/* Sidebar content */ +.sidebar a { + font-weight: normal; + color: #fff; +} +.sidebar-item { + padding: 1rem; +} +.sidebar-item p:last-child { + margin-bottom: 0; +} + +/* Sidebar nav */ +.sidebar-nav { + border-bottom: 1px solid rgba(255,255,255,.1); +} +.sidebar-nav-item { + display: block; + padding: .5rem 1rem; + border-top: 1px solid rgba(255,255,255,.1); +} +.sidebar-nav-item.active, +a.sidebar-nav-item:hover, +a.sidebar-nav-item:focus { + text-decoration: none; + background-color: rgba(255,255,255,.1); + border-color: transparent; +} + +@media (min-width: 48em) { + .sidebar-item { + padding: 1.5rem; + } + .sidebar-nav-item { + padding-left: 1.5rem; + padding-right: 1.5rem; + } +} + +/* Hide the sidebar checkbox that we toggle with `.sidebar-toggle` */ +.sidebar-checkbox { + position: absolute; + opacity: 0; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +/* Style the `label` that we use to target the `.sidebar-checkbox` */ +.sidebar-toggle { + position: absolute; + top: .8rem; + left: 1rem; + display: flex; + align-items: center; + padding: .25rem .75rem; + color: #505050; + background-color: #fff; + border-radius: .25rem; + cursor: pointer; +} + +.sidebar-toggle::before { + display: inline-block; + width: 32px; + height: 32px; + content: ""; + background: url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' fill='%23555' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' d='M2.5 11.5A.5.5 0 013 11h10a.5.5 0 010 1H3a.5.5 0 01-.5-.5zm0-4A.5.5 0 013 7h10a.5.5 0 010 1H3a.5.5 0 01-.5-.5zm0-4A.5.5 0 013 3h10a.5.5 0 010 1H3a.5.5 0 01-.5-.5z' clip-rule='evenodd'/%3E%3C/svg%3E") no-repeat; +} + +.sidebar-toggle:active, +#sidebar-checkbox:focus ~ .sidebar-toggle, +#sidebar-checkbox:checked ~ .sidebar-toggle { + color: #fff; + background-color: #555; +} + +.sidebar-toggle:active:before, +#sidebar-checkbox:focus ~ .sidebar-toggle::before, +#sidebar-checkbox:checked ~ .sidebar-toggle::before { + background: url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' d='M2.5 11.5A.5.5 0 013 11h10a.5.5 0 010 1H3a.5.5 0 01-.5-.5zm0-4A.5.5 0 013 7h10a.5.5 0 010 1H3a.5.5 0 01-.5-.5zm0-4A.5.5 0 013 3h10a.5.5 0 010 1H3a.5.5 0 01-.5-.5z' clip-rule='evenodd'/%3E%3C/svg%3E") no-repeat; +} + +@media (min-width: 30.1em) { + .sidebar-toggle { + position: fixed; + } +} + +@media print { + .sidebar-toggle { + display: none; + } +} + +/* Slide effect + * + * Handle the sliding effects of the sidebar and content in one spot, seperate + * from the default styles. + * + * As an a heads up, we don't use `transform: translate3d()` here because when + * mixed with `position: fixed;` for the sidebar toggle, it creates a new + * containing block. Put simply, the fixed sidebar toggle behaves like + * `position: absolute;` when transformed. + * + * Read more about it at http://meyerweb.com/eric/thoughts/2011/09/12/. + */ + +.wrap, +.sidebar, +.sidebar-toggle { + -webkit-backface-visibility: hidden; + -ms-backface-visibility: hidden; + backface-visibility: hidden; +} +.wrap, +.sidebar-toggle { + -webkit-transition: -webkit-transform .3s ease-in-out; + transition: transform .3s ease-in-out; +} + +#sidebar-checkbox:checked + .sidebar { + z-index: 10; + visibility: visible; +} +#sidebar-checkbox:checked ~ .sidebar, +#sidebar-checkbox:checked ~ .wrap, +#sidebar-checkbox:checked ~ .sidebar-toggle { + -webkit-transform: translateX(14rem); + -ms-transform: translateX(14rem); + transform: translateX(14rem); +} + + +/* + * Posts and pages + * + * Each post is wrapped in `.post` and is used on default and post layouts. Each + * page is wrapped in `.page` and is only used on the page layout. + */ + +.page, +.post { + margin-bottom: 4em; +} + +/* Blog post or page title */ +.page-title, +.post-title, +.post-title a { + color: #303030; +} +.page-title, +.post-title { + margin-top: 0; +} + +/* Meta data line below post title */ +.post-date { + display: block; + margin-top: -.5rem; + margin-bottom: 1rem; + color: #9a9a9a; +} + +/* Related posts */ +.related { + padding-top: 2rem; + padding-bottom: 2rem; + border-top: 1px solid #eee; +} +.related-posts { + padding-left: 0; + list-style: none; +} +.related-posts h3 { + margin-top: 0; +} +.related-posts li small { + font-size: 75%; + color: #999; +} +.related-posts li a:hover { + color: #268bd2; + text-decoration: none; +} +.related-posts li a:hover small { + color: inherit; +} + + +/* + * Pagination + * + * Super lightweight (HTML-wise) blog pagination. `span`s are provide for when + * there are no more previous or next posts to show. + */ + +.pagination { + overflow: hidden; /* clearfix */ + margin-left: -1rem; + margin-right: -1rem; + font-family: "PT Sans", Helvetica, Arial, sans-serif; + color: #ccc; + text-align: center; +} + +/* Pagination items can be `span`s or `a`s */ +.pagination-item { + display: block; + padding: 1rem; + border: 1px solid #eee; +} +.pagination-item:first-child { + margin-bottom: -1px; +} + +/* Only provide a hover state for linked pagination items */ +a.pagination-item:hover { + background-color: #f5f5f5; +} + +@media (min-width: 30em) { + .pagination { + margin: 3rem 0; + } + .pagination-item { + float: left; + width: 50%; + } + .pagination-item:first-child { + margin-bottom: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + } + .pagination-item:last-child { + margin-left: -1px; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + } +} + + +/* + * Reverse layout + * + * Flip the orientation of the page by placing the `.sidebar` and sidebar toggle + * on the right side. + */ + +.layout-reverse .sidebar { + left: auto; + right: -14rem; +} +.layout-reverse .sidebar-toggle { + left: auto; + right: 1rem; +} + +.layout-reverse #sidebar-checkbox:checked ~ .sidebar, +.layout-reverse #sidebar-checkbox:checked ~ .wrap, +.layout-reverse #sidebar-checkbox:checked ~ .sidebar-toggle { + -webkit-transform: translateX(-14rem); + -ms-transform: translateX(-14rem); + transform: translateX(-14rem); +} + + +/* + * Themes + * + * Apply custom color schemes by adding the appropriate class to the `body`. + * Based on colors from Base16: http://chriskempson.github.io/base16/#default. + */ + +/* Red */ +.theme-base-08 .sidebar, +.theme-base-08 .sidebar-toggle:active, +.theme-base-08 #sidebar-checkbox:checked ~ .sidebar-toggle { + background-color: #ac4142; +} +.theme-base-08 .container a, +.theme-base-08 .sidebar-toggle, +.theme-base-08 .related-posts li a:hover { + color: #ac4142; +} + +/* Orange */ +.theme-base-09 .sidebar, +.theme-base-09 .sidebar-toggle:active, +.theme-base-09 #sidebar-checkbox:checked ~ .sidebar-toggle { + background-color: #d28445; +} +.theme-base-09 .container a, +.theme-base-09 .sidebar-toggle, +.theme-base-09 .related-posts li a:hover { + color: #d28445; +} + +/* Yellow */ +.theme-base-0a .sidebar, +.theme-base-0a .sidebar-toggle:active, +.theme-base-0a #sidebar-checkbox:checked ~ .sidebar-toggle { + background-color: #f4bf75; +} +.theme-base-0a .container a, +.theme-base-0a .sidebar-toggle, +.theme-base-0a .related-posts li a:hover { + color: #f4bf75; +} + +/* Green */ +.theme-base-0b .sidebar, +.theme-base-0b .sidebar-toggle:active, +.theme-base-0b #sidebar-checkbox:checked ~ .sidebar-toggle { + background-color: #90a959; +} +.theme-base-0b .container a, +.theme-base-0b .sidebar-toggle, +.theme-base-0b .related-posts li a:hover { + color: #90a959; +} + +/* Cyan */ +.theme-base-0c .sidebar, +.theme-base-0c .sidebar-toggle:active, +.theme-base-0c #sidebar-checkbox:checked ~ .sidebar-toggle { + background-color: #75b5aa; +} +.theme-base-0c .container a, +.theme-base-0c .sidebar-toggle, +.theme-base-0c .related-posts li a:hover { + color: #75b5aa; +} + +/* Blue */ +.theme-base-0d .sidebar, +.theme-base-0d .sidebar-toggle:active, +.theme-base-0d #sidebar-checkbox:checked ~ .sidebar-toggle { + background-color: #6a9fb5; +} +.theme-base-0d .container a, +.theme-base-0d .sidebar-toggle, +.theme-base-0d .related-posts li a:hover { + color: #6a9fb5; +} + +/* Magenta */ +.theme-base-0e .sidebar, +.theme-base-0e .sidebar-toggle:active, +.theme-base-0e #sidebar-checkbox:checked ~ .sidebar-toggle { + background-color: #aa759f; +} +.theme-base-0e .container a, +.theme-base-0e .sidebar-toggle, +.theme-base-0e .related-posts li a:hover { + color: #aa759f; +} + +/* Brown */ +.theme-base-0f .sidebar, +.theme-base-0f .sidebar-toggle:active, +.theme-base-0f #sidebar-checkbox:checked ~ .sidebar-toggle { + background-color: #8f5536; +} +.theme-base-0f .container a, +.theme-base-0f .sidebar-toggle, +.theme-base-0f .related-posts li a:hover { + color: #8f5536; +} + + +/* + * Overlay sidebar + * + * Make the sidebar content overlay the viewport content instead of pushing it + * aside when toggled. + */ + +.sidebar-overlay #sidebar-checkbox:checked ~ .wrap { + -webkit-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); +} +.sidebar-overlay #sidebar-checkbox:checked ~ .sidebar-toggle { + box-shadow: 0 0 0 .25rem #fff; +} +.sidebar-overlay #sidebar-checkbox:checked ~ .sidebar { + box-shadow: .25rem 0 .5rem rgba(0,0,0,.1); +} + +/* Only one tweak for a reverse layout */ +.layout-reverse.sidebar-overlay #sidebar-checkbox:checked ~ .sidebar { + box-shadow: -.25rem 0 .5rem rgba(0,0,0,.1); +} diff --git a/css/syntax.css b/css/syntax.css new file mode 100644 index 0000000..6ba878e --- /dev/null +++ b/css/syntax.css @@ -0,0 +1,62 @@ +a.sourceLine { display: inline-block; line-height: 1.25; } +.container .sourceCode a.sourceLine { pointer-events: none; color: #313131;} +a.sourceLine:empty { height: 1.2em; } +.sourceCode { overflow: auto; } +code.sourceCode { white-space: pre; position: relative; } +div.sourceCode { margin: 1em 0; } +pre.sourceCode { margin: 0; } +@media screen { +div.sourceCode { overflow: auto; } +} +@media print { +code.sourceCode { white-space: pre-wrap; } +a.sourceLine { text-indent: -1em; padding-left: 1em; } +} +pre.numberSource a.sourceLine + { position: relative; left: -4em; } +pre.numberSource a.sourceLine::before + { content: attr(title); + position: relative; left: -1em; text-align: right; vertical-align: baseline; + border: none; pointer-events: all; display: inline-block; + -webkit-touch-callout: none; -webkit-user-select: none; + -khtml-user-select: none; -moz-user-select: none; + -ms-user-select: none; user-select: none; + padding: 0 4px; width: 4em; + color: #aaaaaa; + } +/* pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; } */ +pre.numberSource { padding: 2rem; } */ +div.sourceCode + { } +@media screen { +a.sourceLine::before { text-decoration: underline; } +} +code span.al { color: #ff0000; font-weight: bold; } /* Alert */ +code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */ +code span.at { color: #7d9029; } /* Attribute */ +code span.bn { color: #40a070; } /* BaseN */ +code span.bu { } /* BuiltIn */ +code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */ +code span.ch { color: #4070a0; } /* Char */ +code span.cn { color: #880000; } /* Constant */ +code span.co { color: #60a0b0; font-style: italic; } /* Comment */ +code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */ +code span.do { color: #ba2121; font-style: italic; } /* Documentation */ +code span.dt { color: #902000; } /* DataType */ +code span.dv { color: #40a070; } /* DecVal */ +code span.er { color: #ff0000; font-weight: bold; } /* Error */ +code span.ex { } /* Extension */ +code span.fl { color: #40a070; } /* Float */ +code span.fu { color: #06287e; } /* Function */ +code span.im { } /* Import */ +code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */ +code span.kw { color: #007020; font-weight: bold; } /* Keyword */ +code span.op { color: #666666; } /* Operator */ +code span.ot { color: #007020; } /* Other */ +code span.pp { color: #bc7a00; } /* Preprocessor */ +code span.sc { color: #4070a0; } /* SpecialChar */ +code span.ss { color: #bb6688; } /* SpecialString */ +code span.st { color: #4070a0; } /* String */ +code span.va { color: #19177c; } /* Variable */ +code span.vs { color: #4070a0; } /* VerbatimString */ +code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */ diff --git a/error/404.rst b/error/404.rst new file mode 100644 index 0000000..d73b743 --- /dev/null +++ b/error/404.rst @@ -0,0 +1,12 @@ +--- +layout: default +title: "404: Page not found" +permalink: 404.html +--- + +404: Page not found +=================== + +Sorry, we've misplaced that URL or it's pointing to something that doesn't exist. `Head back home`_ to try finding it again. + +.. _Head back home: $site-url$ diff --git a/images/hakyll-logo.svg b/images/hakyll-logo.svg new file mode 100644 index 0000000..0408c38 --- /dev/null +++ b/images/hakyll-logo.svg @@ -0,0 +1,85 @@ + + + + diff --git a/images/haskell-logo.png b/images/haskell-logo.png new file mode 100644 index 0000000..97c0937 Binary files /dev/null and b/images/haskell-logo.png differ diff --git a/jfdic-web.cabal b/jfdic-web.cabal index 78cd4e6..2b3950e 100644 --- a/jfdic-web.cabal +++ b/jfdic-web.cabal @@ -7,6 +7,8 @@ executable site main-is: site.hs build-depends: base == 4.* , hakyll == 4.14.* + , containers == 0.6.* + , filepath == 1.4.* , pandoc ghc-options: -threaded default-language: Haskell2010 diff --git a/js/script.js b/js/script.js new file mode 100644 index 0000000..b47910a --- /dev/null +++ b/js/script.js @@ -0,0 +1,15 @@ +(function(document) { + var toggle = document.querySelector('.sidebar-toggle'); + var sidebar = document.querySelector('#sidebar'); + var checkbox = document.querySelector('#sidebar-checkbox'); + + document.addEventListener('click', function(e) { + var target = e.target; + + if(!checkbox.checked || + sidebar.contains(target) || + (target === checkbox || target === toggle)) return; + + checkbox.checked = false; + }, false); +})(document); diff --git a/about.rst b/pages/About.rst similarity index 100% rename from about.rst rename to pages/About.rst diff --git a/contact.rst b/pages/Contact.rst similarity index 100% rename from contact.rst rename to pages/Contact.rst diff --git a/site.hs b/site.hs index ee9443b..9fe7cd4 100644 --- a/site.hs +++ b/site.hs @@ -1,13 +1,16 @@ -------------------------------------------------------------------------------- {-# LANGUAGE OverloadedStrings #-} -import Data.Monoid (mappend) +import Data.Monoid (mappend) +import Data.List (sortBy) +import Data.Ord (comparing) import Hakyll - +import Control.Monad (liftM, forM_) +import System.FilePath (takeBaseName) -------------------------------------------------------------------------------- main :: IO () main = hakyll $ do - match "images/*" $ do + match ("images/*" .||. "js/*") $ do route idRoute compile copyFileCompiler @@ -15,52 +18,181 @@ main = hakyll $ do route idRoute compile compressCssCompiler - match (fromList ["about.rst", "contact.rst"]) $ do - route $ setExtension "html" + match "error/*" $ do + route $ (gsubRoute "error/" (const "") `composeRoutes` setExtension "html") compile $ pandocCompiler - >>= loadAndApplyTemplate "templates/default.html" defaultContext - >>= relativizeUrls + >>= applyAsTemplate siteCtx + >>= loadAndApplyTemplate "templates/default.html" (baseSidebarCtx <> siteCtx) + + match "pages/*" $ do + route $ setExtension "html" + compile $ do + pageName <- takeBaseName . toFilePath <$> getUnderlying + let pageCtx = constField pageName "" `mappend` + baseNodeCtx + let evalCtx = functionField "get-meta" getMetadataKey `mappend` + functionField "eval" (evalCtxKey pageCtx) + let activeSidebarCtx = sidebarCtx (evalCtx <> pageCtx) + + pandocCompiler + >>= saveSnapshot "page-content" + >>= loadAndApplyTemplate "templates/page.html" siteCtx + >>= loadAndApplyTemplate "templates/default.html" (activeSidebarCtx <> siteCtx) + >>= relativizeUrls + + tags <- buildTags "posts/*" (fromCapture "tags/*.html") + + match "posts/*" $ version "meta" $ do + route $ setExtension "html" + compile getResourceBody match "posts/*" $ do route $ setExtension "html" - compile $ pandocCompiler - >>= loadAndApplyTemplate "templates/post.html" postCtx - >>= loadAndApplyTemplate "templates/default.html" postCtx - >>= relativizeUrls + compile $ do + posts <- loadAll ("posts/*" .&&. hasVersion "meta") + let taggedPostCtx = (tagsField "tags" tags) `mappend` + postCtx `mappend` + (relatedPostsCtx posts 3) + + pandocCompiler + >>= saveSnapshot "content" + >>= loadAndApplyTemplate "templates/post.html" taggedPostCtx + >>= loadAndApplyTemplate "templates/default.html" (baseSidebarCtx <> siteCtx) + >>= relativizeUrls create ["archive.html"] $ do route idRoute compile $ do - posts <- recentFirst =<< loadAll "posts/*" + posts <- recentFirst =<< loadAllSnapshots ("posts/*" .&&. hasNoVersion) "content" let archiveCtx = listField "posts" postCtx (return posts) `mappend` - constField "title" "Archives" `mappend` - defaultContext + constField "title" "Archive" `mappend` + constField "archive" "" `mappend` + siteCtx makeItem "" >>= loadAndApplyTemplate "templates/archive.html" archiveCtx - >>= loadAndApplyTemplate "templates/default.html" archiveCtx + >>= loadAndApplyTemplate "templates/default.html" (baseSidebarCtx <> archiveCtx) >>= relativizeUrls + paginate <- buildPaginateWith postsGrouper "posts/*" postsPageId - match "index.rst" $ do - route $ setExtension "html" + paginateRules paginate $ \page pattern -> do + route idRoute compile $ do - posts <- recentFirst =<< loadAll "posts/*" + posts <- recentFirst =<< loadAllSnapshots (pattern .&&. hasNoVersion) "content" let indexCtx = + constField "title" (if page == 1 then "Home" + else "Blog posts, page " ++ show page) `mappend` listField "posts" postCtx (return posts) `mappend` - defaultContext + constField "home" "" `mappend` + paginateContext paginate page `mappend` + siteCtx - pandocCompiler + makeItem "" >>= applyAsTemplate indexCtx - >>= loadAndApplyTemplate "templates/default.html" indexCtx + >>= loadAndApplyTemplate "templates/index.html" indexCtx + >>= loadAndApplyTemplate "templates/default.html" (baseSidebarCtx <> indexCtx) >>= relativizeUrls match "templates/*" $ compile templateBodyCompiler + create ["atom.xml"] $ do + route idRoute + compile $ do + let feedCtx = postCtx `mappend` + bodyField "description" + posts <- fmap (take 10) . recentFirst =<< loadAllSnapshots ("posts/*" .&&. hasNoVersion) "content" + renderAtom feedConfig feedCtx posts -------------------------------------------------------------------------------- + +postsGrouper :: MonadFail m => MonadMetadata m => [Identifier] -> m [[Identifier]] +postsGrouper = liftM (paginateEvery 3) . sortRecentFirst + + +postsPageId :: PageNumber -> Identifier +postsPageId n = fromFilePath $ if (n == 1) then "index.html" else show n ++ "/index.html" + +-------------------------------------------------------------------------------- + +feedConfig :: FeedConfiguration +feedConfig = FeedConfiguration + { feedTitle = "JFDI Collective" + , feedDescription = "Just Effing Doing It" + , feedAuthorName = "JFDI Collective" + , feedAuthorEmail = "collective@jfdic.org" + , feedRoot = "https://jfdic.org" + } + +-------------------------------------------------------------------------------- + +siteCtx :: Context String +siteCtx = + baseCtx `mappend` + constField "site_description" "JFDI Collective" `mappend` + constField "site-url" "https://jfdic.org" `mappend` + constField "tagline" "Just Effing Doing It" `mappend` + constField "site-title" "JFDI Collective" `mappend` + constField "copy-year" "2021" `mappend` + constField "github-repo" "https://source.jfdic.org/jfdic/jfdic-web" `mappend` + defaultContext + +baseCtx = + constField "baseurl" "http://localhost:8000" + +-------------------------------------------------------------------------------- + postCtx :: Context String postCtx = dateField "date" "%B %e, %Y" `mappend` defaultContext + +tagsRulesVersioned tags rules = + forM_ (tagsMap tags) $ \(tag, identifiers) -> + rulesExtraDependencies [tagsDependency tags] $ + create [tagsMakeId tags tag] $ + rules tag identifiers + +relatedPostsCtx + :: [Item String] -> Int -> Context String +relatedPostsCtx posts n = listFieldWith "related_posts" postCtx selectPosts + where + rateItem ts i = length . filter (`elem` ts) <$> (getTags $ itemIdentifier i) + selectPosts s = do + postTags <- getTags $ itemIdentifier s + let trimmedItems = filter (not . matchPath s) posts + take n . reverse <$> sortOnM (rateItem postTags) trimmedItems + +matchPath :: Item String -> Item String -> Bool +matchPath x y = eqOn (toFilePath . itemIdentifier) x y + +eqOn :: Eq b => (a -> b) -> a -> a -> Bool +eqOn f x y = f x == f y + +sortOnM :: (Monad m, Ord b) => (a -> m b) -> [a] -> m [a] +sortOnM f xs = map fst . sortBy (comparing snd) . zip xs <$> mapM f xs + +-------------------------------------------------------------------------------- + +sidebarCtx :: Context String -> Context String +sidebarCtx nodeCtx = + listField "list_pages" nodeCtx (loadAllSnapshots ("pages/*" .&&. hasNoVersion) "page-content") `mappend` + defaultContext + +baseNodeCtx :: Context String +baseNodeCtx = + urlField "node-url" `mappend` + titleField "title" `mappend` + baseCtx + +baseSidebarCtx = sidebarCtx baseNodeCtx + +evalCtxKey :: Context String -> [String] -> Item String -> Compiler String +evalCtxKey context [key] item = (unContext context key [] item) >>= \cf -> + case cf of + StringField s -> return s + _ -> error $ "Internal error: StringField expected" + +getMetadataKey :: [String] -> Item String -> Compiler String +getMetadataKey [key] item = getMetadataField' (itemIdentifier item) key diff --git a/templates/archive.html b/templates/archive.html index 39459bd..8a4c8d7 100644 --- a/templates/archive.html +++ b/templates/archive.html @@ -1,2 +1,4 @@ -Here you can find all our previous posts: -$partial("templates/post-list.html")$ +