/// Replace `$search` with `$replace` in `$string` /// @author Hugo Giraudel /// @param {String} $string - Initial string /// @param {String} $search - Substring to replace /// @param {String} $replace ('') - New value /// @return {String} - Updated string @function str-replace($string, $search, $replace: '') { $index: str-index($string, $search); @if $index { @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); } @return $string; } // @source https://gist.github.com/B-iggy/14da053d2cebf92e1930 // @author B-iggy @function url-encode($string) { $map: ( "%": "%25", "<": "%3C", ">": "%3E", " ": "%20", "!": "%21", "*": "%2A", "'": "%27", '"': "%22", "(": "%28", ")": "%29", ";": "%3B", ":": "%3A", "@": "%40", "&": "%26", "=": "%3D", "+": "%2B", "$": "%24", ",": "%2C", "/": "%2F", "?": "%3F", "#": "%23", "[": "%5B", "]": "%5D" ); $new: $string; @each $search, $replace in $map { $new: str-replace($new, $search, $replace); } @return $new; } /// declare a cross-browser @font-face. /// note that Firefox is very picky about the syntax here: experimentation shows that /// the eot MUST come first (even if FF doesn't load it), and the other formats MUST /// be declared under one `src` directive. the local sources may have a separate `src`. /// @author @colin@fed.uninsane.org @mixin font-face($family, $variant, $short, $weight, $style) { $path: 'fonts/' + str-replace($family, ' ', '') + '/' + $short; $hyphenated: str-replace($variant, ' ', '-'); @font-face { font-family: $family; src: url('#{$path}.eot'); src: url('#{$path}.eot?#iefix') format('embedded-opentype'), url('#{$path}.woff2') format('woff2'), url('#{$path}.woff') format('woff'), url('#{$path}.ttf') format('truetype'); // TODO: local fonts disabled during devel // src: local($variant), local($hyphenated); font-weight: $weight; font-style: $style; } } @function hsb($h-hsb, $s-hsb, $b-hsb, $a: 1) { @if $b-hsb == 0 { @return hsla(0, 0, 0, $a) } @else { $l-hsl: ($b-hsb/2) * (2 - ($s-hsb/100)); $s-hsl: ($b-hsb * $s-hsb) / if($l-hsl < 50, $l-hsl * 2, 200 - $l-hsl * 2); @return hsla($h-hsb, $s-hsl, $l-hsl, $a); } } // naming: lower number = brighter, higher number = darker $color_cream_1: hsb(27, 10%, 100%); $color_cream_2: hsb(19, 22%, 97%); //$color_cream_3: hsb(3, 25%, 91%); $color_cream_4: hsb(17, 13%, 100%); $color_cream_5: hsb(17, 16%, 85%); //$color_teal_1: hsb(191, 15%, 91%); //$color_teal_2: hsb(191, 55%, 35%); //$color_purple_0: hsb(268, 11%, 93%); //$color_purple_1: hsb(252, 15%, 95%); //$color_purple_2: hsb(252, 20%, 90%); //$color_purple_3: hsb(252, 55%, 45%); //$color_pink_2: hsb(329, 63%, 79%); $color_pink_1: hsb(3, 9%, 99%); //$color_pink_15:hsb(329, 21%, 88%); $color_pink_2: hsb(345, 23%, 83%); $color_pink_3: hsb(345, 35%, 70%); $color_pink_4: hsb(337, 63%, 60%); $color_pink_5: hsb(347, 67%, 50%); $color_bg: $color_cream_1; $color_h1: $color_pink_1; $color_h2: $color_cream_4; $color_link: $color_pink_5; $color_link_shadow: $color_pink_2; $color_link_hover: $color_pink_3; $font_scale_hdr: 1.50; $indent_hdr: 1.3rem; // effective margin of h1, h2 $margin_hdr_max: $indent_hdr; $margin_hdr_min: 0.2rem; $margin_hdr_delta: $margin_hdr_max - $margin_hdr_min; // TODO: move these into files and #include them. // MIT; source: https://icon-sets.iconify.design/bi/arrow-return-right/ $icon_bootstrap_arrow_return_right: url('data:image/svg+xml; utf8, '); // MIT; source: https://icon-sets.iconify.design/bi/arrow-right/ $icon_bootstrap_arrow_right: url('data:image/svg+xml; utf8, '); // MIT; source: https://icon-sets.iconify.design/bi/arrow-bar-right/ $icon_bootstrap_arrow_bar_right: url('data:image/svg+xml; utf8, '); // @author colin // made in Inkscape $icon_h2_slash_svg: ' '; $icon_h2_slash: url("data:image/svg+xml; utf8, #{url-encode($icon_h2_slash_svg)}"); // CC0; based on: https://www.svgrepo.com/svg/211498/rss $icon_rss_svg: ' '; $icon_rss: url("data:image/svg+xml; utf8, #{url-encode($icon_rss_svg)}"); $marker_title: $icon_bootstrap_arrow_return_right; $marker_section: $icon_bootstrap_arrow_right; $marker_li: "⤚ "; // list of arrows: https://www.alt-codes.net/arrow_alt_codes.php // ↳ // ⤷ // ➥ // // → // ↦ // ↣ // ⇒ // ⇢ // ⇥ // ⇨ // ⍈ // ➜ // ➔ // ➙ // ⤚ // ⤞ // ⤠ // ➳ // ► // ➢ // ➲ // reset browser defaults body { padding: 0; margin-left: 0; margin-right: 0; } h1, h2, h3, header { font-size: $font_scale_hdr * 1rem; padding: 0; margin-left: 0; margin-right: 0; } body { background-color: $color_bg; font-family: "Gentium Basic","serif"; } :link, :visited { text-decoration: none; color: $color_link; } :link:hover { color: $color_link_hover; } .flex-right { text-align: right; flex-grow: 1000; } .portal { max-width: 40rem; // center this within its container margin-left: auto; margin-right: auto; p, ul, pre { text-align: justify; padding: 0; // this aligns the paragraphs with the header indents margin-left: $margin_hdr_max; margin-right: $margin_hdr_max; // interpolate margins from 0 (at 20em) -> max (at 40em): @media (max-width: 40rem) { margin-left: calc(#{$margin_hdr_delta / 20rem * 100%} + #{$margin_hdr_min - $margin_hdr_delta}); margin-right: calc(#{$margin_hdr_delta / 20rem * 100%} + #{$margin_hdr_min - $margin_hdr_delta}); } @media (max-width: 20rem) { margin-left: $margin_hdr_min; margin-right: $margin_hdr_min; } } img { max-width: 100%; } ul { padding-left: 1.3rem; } li::marker { content: $marker_li; } .date { font-style: normal; // dates should be able to render as subtitles below headings, gapless margin-top: 0; } header, h1, h2, h3 { display: flex; } header { a { font-style: italic; } .link-consolidated { border-bottom: 2px solid $color_link_shadow; } .link-consolidated-left { padding-left: 0.2rem; padding-right: 0.6rem; } .link-consolidated-right { padding-left: 0.6rem; padding-right: 0.2rem; } .link-rss { content: $icon_rss; width: 1rem; height: 1rem; margin-left: 0.8rem; margin-bottom: -0.15rem; } } // the first heading after the global header should have no gap header + * > h1 { margin-top: 0; // since it's followed by the date, it shouldn't enforce a bottom margin either margin-bottom: 0.5rem; } .body-text { pre { // create a scrollbar when the content overflows x. overflow-x: auto; // subtle rounding of the background edges border-radius: 2px; // scrollbar-color: grey #2b303b; code { display: block; margin: 0.15rem; } } h1 { // this extra padding accomodates the underlines from the header padding-top: 2px; padding-bottom: 2px; background-color: $color_h1; } h2, h3 { padding-top: 2px; padding-bottom: 2px; background-color: $color_h2; } h1::after, h2::after, h3::after { content: ""; flex-grow: 1; min-width: $indent_hdr - 0.3rem; margin-left: 0.15rem; margin-right: 0.15rem; } h1::before, h2::before, h3::before { content: ""; font-size: 1.35rem; width: $indent_hdr - 0.3rem; margin-left: 0.15rem; margin-right: 0.15rem; } h1::before { content: $marker_title; font-size: 1.5rem; } h2::before { content: $marker_section; font-size: 1.5rem; } h2::after { background-image: $icon_h2_slash; background-repeat: repeat-x; } // TODO colin: numbers are slightly wrong because of scrollbar width. // could adjust these to make the transition smoother. // // at low width, allow the left padding on section headers to shrink $h_disappear_max: 40rem; $h_disappear_delta: 2*($indent_hdr - 0.3rem); $h_disappear_min: $h_disappear_max - $h_disappear_delta; @media (max-width: #{$h_disappear_max}) { h1::before, h2::before, h3::before { max-width: calc(50% - #{$h_disappear_min * 0.5 - 0.5rem}); } h1::after, h2::after { min-width: calc(50% - #{$h_disappear_min * 0.5}); } } // we need to hide the left-side decor as the padding shrinks @media (max-width: #{$h_disappear_max - 1rem}) { h1::before, h2::before, h3::before { // zeroing the opacity hides this without changing any of the page formatting opacity: 0; } } } .listing { .title { font-size: 1.3rem; margin-top: 0.5rem; } .subtitle { font-style: italic; font-size: 0.9rem; margin-left: 1rem; margin-top: 0.1rem; } } } /* font: Gentium Basic * upstream: https://www.fontsquirrel.com/fonts/Gentium-Basic * https://fonts.google.com/specimen/Gentium+Basic * license: SIL Open Font License 1.1 * author: SIL International */ @include font-face('Gentium Basic', 'Gentium Basic', 'GenBasR', 400, normal) @include font-face('Gentium Basic', 'Gentium Basic Bold', 'GenBasB', 700, normal) @include font-face('Gentium Basic', 'Gentium Basic Italic', 'GenBasI', 400, italic) @include font-face('Gentium Basic', 'Gentium Basic Bold Italic', 'GenBasBI', 700, italic)