Convert stylesheet to SCSS and preprocess with SASS.
authorNick Bowler <nbowler@draconx.ca>
Thu, 27 Feb 2020 18:50:46 +0000 (13:50 -0500)
committerNick Bowler <nbowler@draconx.ca>
Sun, 21 Jun 2020 16:52:20 +0000 (12:52 -0400)
This CSS preprocessing tool seems pretty helpful for reducing repetition
in stylesheets.  While it does not make much difference now I hope to
make more use of its features in the future.

.gitignore
Rules
content/style.css [deleted file]
content/style.scss [new file with mode: 0644]
lib/css-clean-selectors.rb [new file with mode: 0644]
lib/css-source.rb [new file with mode: 0644]

index b186842aed4d69ab15ebe4b495e954f68629bdfb..0810595e4bb06e5ff923dbc0b0e6ca5387e9ba50 100644 (file)
@@ -1,3 +1,4 @@
+/.sass-cache
 /crash.log
 /output
 /tmp
 /crash.log
 /output
 /tmp
diff --git a/Rules b/Rules
index a7dffeef792cb601a11b44497a7a7c767d520461..d9130f02ca475b712fb4ae28ca965e02454a60a6 100644 (file)
--- a/Rules
+++ b/Rules
@@ -92,6 +92,17 @@ compile '/license/cc*.xhtml' do
     write to_xhtml
 end
 
     write to_xhtml
 end
 
+compile '/**/*.scss' do
+    filter :sass, syntax: :scss
+    filter :css_source, uribase: \
+        "https://git.draconx.ca/gitweb/homepage.git/blob/" +
+        @item[:gitrev] + ":"
+    filter :css_clean_selectors, \
+        preserve_comments: true, \
+        preserve_hacks: true
+    write @item.identifier.without_ext + '.css'
+end
+
 passthrough '/**/*'
 layout '/**/*.xsl', :xsl
 layout '/**/*', :erb
 passthrough '/**/*'
 layout '/**/*.xsl', :xsl
 layout '/**/*', :erb
diff --git a/content/style.css b/content/style.css
deleted file mode 100644 (file)
index 1028de0..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Nick's web site: default stylesheet
- *
- * Copyright © 2018 Nick Bowler
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-body {
-    font-family: sans-serif;
-    margin: 1em;
-}
-
-h1 {
-    font-size: 2em;
-    max-width: 30em; /* 60rem */
-}
-
-h2 {
-    font-size: 1.5em;
-    max-width: 40em; /* 60rem */
-}
-
-a:link {
-    color: mediumblue;
-}
-
-a:active {
-    color: red;
-}
-
-a:visited {
-    color: purple;
-}
-
-p, body>div {
-    text-align: justify;
-    padding: 0;
-    margin: 1em 0;
-    max-width: 50em;
-}
-
-ul, ol, dl {
-    max-width: 50em;
-    padding: 0;
-    margin: 0;
-}
-
-dt, dd, li {
-    text-align: justify;
-    padding: 0;
-}
-li { margin: 0 0 0 2em; }
-dd { margin: 0 0 0 1em; }
-
-ul.ordered>li {
-    list-style: none;
-}
-
-ul.ordered>li>span:first-child, ul.ordered>li>*:first-child>span:first-child {
-    display: inline-block;
-    text-align: right;
-
-    margin-left: -1.8em;
-    min-width: 1.8em;
-}
-
-hr {
-    clear: both;
-    text-align: left;
-    margin: 0.5em 0;
-    max-width: 50em;
-    border: 0;
-    border-top: 1px solid lightgray;
-}
-
-small.permalink {
-    font-size: small;
-    color: slategray;
-}
-.permalink a:link, .permalink a:visited {
-    color: slategray;
-}
-
-@media not screen {
-    small.permalink {
-        visibility: hidden;
-    }
-}
-
-#footer {
-    max-width: 44em;
-    margin: 0;
-    padding: 0 3em;
-    text-align: center;
-}
-
-#footer>p {
-    display: inline-block;
-    color: slategray;
-    font-size: 0.8em;
-    max-width: 100%;
-    margin: 0.2em 0;
-}
-
-kbd {
-    font-family: monospace;
-    font-size: small;
-}
-
-kbd::before {
-    content: "% ";
-}
-
-#breadcrumbs, #sitetitle { text-align: left; }
-#breadcrumbs>*, #sitetitle>* { font-size: 0.8em; }
-
-#sitetitle>* {
-    float: right;
-    display: inline-block;
-}
-
-#breadcrumbs>strong {
-    vertical-align: top;
-    margin: 0;
-    padding: 0;
-}
-
-#breadcrumbs>ul {
-    list-style-type: none;
-    vertical-align: top;
-    display: inline;
-    margin: 0;
-    padding: 0;
-}
-
-#breadcrumbs>ul>li {
-    display: inline;
-    padding: 0;
-    margin: 0;
-}
-
-#breadcrumbs>ul>li::before { content: "/ " }
-#breadcrumbs>ul>li:first-child::before { content: none; }
-
-@media (max-width: 512px) {
-    body { margin: 0.6em; }
-    ul ul { margin-left: -1.2em; }
-    dd { margin: 0; }
-}
-
-@media (max-width: 28em) { li, dd, dt, p { text-align: left; } }
-@media (max-width: 35em) {
-    #sitetitle>* { float: none; }
-    #footer { padding: 0 1em; }
-}
diff --git a/content/style.scss b/content/style.scss
new file mode 100644 (file)
index 0000000..79d4ffb
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Nick's web site: default stylesheet
+ *
+ * Copyright © 2018-2020 Nick Bowler
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+// colour definitions
+$backgroundcolour:  #ffffff;
+$foregroundcolour:  #000000;
+
+$linkdefaultcolour: #0000cd;
+$linkactivecolour:  #ff0000;
+$linkvisitedcolour: #800080;
+
+$ruledefaultcolour: #d3d3d3;
+
+$annotationcolour:  #708090;
+
+@mixin header_size($maxwidth, $fontsize) {
+    font-size: $fontsize;
+    max-width: 1em * ($maxwidth / $fontsize);
+}
+
+body {
+    background-color: $backgroundcolour;
+    color: $foregroundcolour;
+    font-family: sans-serif;
+    margin: 1em;
+}
+
+a:link { color: $linkdefaultcolour; }
+a:visited { color: $linkvisitedcolour; }
+a:active { color: $linkactivecolour; }
+
+h1 { @include header_size(60em, 2em); }
+h2 { @include header_size(60em, 1.5em); }
+
+p, dt, dd, li {
+    text-align: justify;
+    padding: 0;
+    margin: 0;
+}
+
+p, div, ul, ol, dl, hr {
+    max-width: 50em;
+    padding: 0;
+    margin: 0;
+}
+
+p, body>div { margin: 1em 0; }
+
+li { margin: 0 0 0 2em; }
+dd { margin: 0 0 0 1em; }
+
+hr {
+    clear: both;
+    margin: 0.5em 0;
+    border: 0;
+    border-top: 1px solid $ruledefaultcolour;
+}
+
+kbd {
+    font-family: monospace;
+    font-size: 0.95em;
+    &:before { content: "% "; }
+}
+
+.permalink {
+    font-size: small;
+    color: $annotationcolour;
+
+    a:link, a:visited { color: inherit; }
+}
+@media not screen {
+    .permalink { visibility: hidden; }
+}
+
+// Site header rules
+#breadcrumbs>*, #sitetitle>* { font-size: 0.8em; }
+#breadcrumbs {
+    * {
+        display: inline;
+        list-style-type: none;
+        vertical-align: top;
+        padding: 0;
+        margin: 0;
+    }
+
+    li + li:before { content: "/ "; }
+}
+#sitetitle * {
+    display: inline-block;
+    float: right;
+}
+
+// Site footer rules
+#footer {
+    text-align: center;
+    max-width: 44em;
+    padding: 0 3em;
+    margin: 0;
+
+    p {
+        color: $annotationcolour;
+        display: inline-block;
+        font-size: 0.8em;
+        max-width: 100%;
+        margin: 0.2em 0;
+    }
+}
+
+// "unordered" lists with explicit ordering in content
+ul.ordered > {
+    li { list-style: none; }
+    li>span:first-child, li>*:first-child>span:first-child {
+        display: inline-block;
+        text-align: right;
+        margin-left: -1.8em;
+        min-width: 1.8em;
+    }
+}
+
+@media (max-width: 512px) {
+    body { margin: 0.6em; }
+    ul ul { margin-left: -1.2em; }
+    dd { margin: 0; }
+}
+
+@media (max-width: 35em) {
+    #sitetitle * { float: none; }
+    #footer { padding: 0 1em; }
+}
+@media (max-width: 28em) {
+    * { text-align: left; }
+}
diff --git a/lib/css-clean-selectors.rb b/lib/css-clean-selectors.rb
new file mode 100644 (file)
index 0000000..7eebab7
--- /dev/null
@@ -0,0 +1,53 @@
+# Nick's web site: css_clean_selectors filter.  Remove unnecessary whitespace
+# from CSS selectors as this improves compatibility with old versions of IE.
+#
+# Copyright © 2020 Nick Bowler
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+class CssCleanSelectorsFilter < Nanoc::Filter
+    identifier :css_clean_selectors
+
+    require 'crass'
+
+    def run(content, params = {})
+        tree = Crass.parse(content, params)
+
+        queue = Array.new(tree)
+        while i = queue.shift do
+          if i[:node] == :at_rule
+            case i[:name]
+            when "media", "supports"
+              # Re-parse the block as a list of rules.
+              s = Crass::Parser.stringify(i[:block])
+              i[:block] = Crass::Parser.parse_rules(s, params)
+              queue += i[:block]
+            end
+          end
+
+          if i[:selector]
+            ts = i[:selector][:tokens]
+            ts.each_index do |i|
+                next if ts[i].nil? or ts[i][:node] != :delim
+                next if ts[i][:value] == '*'
+
+                ts[i-1] = nil if ts[i-1] and ts[i-1][:node] == :whitespace
+                ts[i+1] = nil if ts[i+1] and ts[i+1][:node] == :whitespace
+            end
+          end
+        end
+
+        return Crass::Parser.stringify(tree)
+    end
+end
diff --git a/lib/css-source.rb b/lib/css-source.rb
new file mode 100644 (file)
index 0000000..211c155
--- /dev/null
@@ -0,0 +1,44 @@
+# Nick's web site: css_source filter.  Add a notice to generated CSS
+# files where the reader may find the source code.
+#
+# Copyright © 2020 Nick Bowler
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+class CssSourceFilter < Nanoc::Filter
+    identifier :css_source
+
+    def run(content, params = {})
+        srcuri = if params[:uribase]
+            then params[:uribase] + item_source(@item) end
+        copypattern = /\/\*.*?[Cc]opyright.*?\*\//m
+        css = content.gsub(copypattern, "")
+        css.gsub!(/^@charset.*?$\n?/m, "")
+
+        begin
+            copymsgs = @item.compiled_content(snapshot: :css_source)
+        rescue Nanoc::Core::Errors::NoSuchSnapshot
+            copymsgs = content
+        end
+        copymsgs = copymsgs.scan(copypattern)
+
+        ["/*",
+         " * Generated file, do not edit!",
+         " * Compiled from " + item_source(@item) + " on " +
+            Time.now.gmtime.strftime("%Y-%m-%d %H:%M UTC"),
+         if srcuri then
+            " * Source code is available online at <" + srcuri + ">"
+         end, "*/", copymsgs, css].compact.join("\n")
+    end
+end