{"id":7629,"date":"2015-09-23T04:59:23","date_gmt":"2015-09-23T04:59:23","guid":{"rendered":"https:\/\/unknownerror.org\/index.php\/2015\/09\/23\/cdaddr-gaka\/"},"modified":"2015-09-23T04:59:23","modified_gmt":"2015-09-23T04:59:23","slug":"cdaddr-gaka","status":"publish","type":"post","link":"https:\/\/unknownerror.org\/index.php\/2015\/09\/23\/cdaddr-gaka\/","title":{"rendered":"cdaddr\/gaka"},"content":{"rendered":"<p>by Brian Carper<\/p>\n<p>Gaka is a CSS-generating library for Clojure inspired partly by Sass and similar to Hiccup.<\/p>\n<h2>Features<\/h2>\n<ul>\n<li>Simple<\/li>\n<li>Indented output<\/li>\n<li>Selector nesting<\/li>\n<li>\u201cMixins\u201d<\/li>\n<\/ul>\n<h2>Purpose<\/h2>\n<p>CSS syntax is verbose, with lots of curly braces and semi-colons. Writing CSS as s-expressions is a way to ensure you have proper syntax, because the program handles the syntax for you. And it lets you write CSS very quickly using an editor that\u2019s good at manipulating s-expressions.<\/p>\n<p>CSS has a lot of repetition in selectors. <code>body #content div a {...}<\/code> etc. You can remove most of this verbosity via nesting. S-expressions are a great way to express that nesting. (Sass uses indentation for the same purpose.)<\/p>\n<p>CSS rules in Gaka are just vectors of keywords and strings and numbers, which means you can easily generate and manipulate them programatically.<\/p>\n<h2>Example<\/h2>\n<p>Rules are vectors, where the first element is a selector and the rest are either key\/value pairs, or sub-rules.<\/p>\n<pre><code>user&gt; (require '(gaka [core :as gaka]))\nnil\nuser&gt; (def rules [:div#foo\n                  :margin \"0px\"\n                  [:span.bar\n                   :color \"black\"\n                   :font-weight \"bold\"\n                   [:a:hover\n                    :text-decoration \"none\"]]])\n#'user\/rules\nuser&gt; (println (gaka\/css rules))\ndiv#foo {\n  margin: 0px;}\n\n  div#foo span.bar {\n    color: black;\n    font-weight: bold;}\n\n    div#foo span.bar a:hover {\n      text-decoration: none;}\n\n\nnil\nuser&gt; (binding [gaka\/*print-indent* false]\n        (println (gaka\/css rules)))\ndiv#foo {\nmargin: 0px;}\n\ndiv#foo span.bar {\ncolor: black;\nfont-weight: bold;}\n\ndiv#foo span.bar a:hover {\ntext-decoration: none;}\n\n\nnil\nuser&gt; (gaka\/save-css \"foo.css\" rules)\nnil\n<\/code><\/pre>\n<p>Anything in a seq (e.g. a list) will be flattened into the surrounding context, which lets you have \u201cmixins\u201d.<\/p>\n<pre><code>user&gt; (def standard-attrs (list :margin 0 :padding 0 :font-size \"12px\"))\n#'user\/standard-attrs\nuser&gt; (println (gaka\/css [:div standard-attrs :color \"red\"]))\ndiv {\n  margin: 0;\n  padding: 0;\n  font-size: 12px;\n  color: red;}\nuser&gt; (defn color [x] (list :color x))\n#'user\/color\nuser&gt; (println (gaka\/css [:div (color \"red\")]))\ndiv {\n  color: red;}\n<\/code><\/pre>\n<p>You can also use maps for attributes. Keep in mind that maps are unordered, whereas order is significant in CSS (attributes can override earlier attributes). To preserve order, either use \u201cflattened\u201d key\/value pairs, or a mixture of map and flattened versions.<\/p>\n<pre><code>;; WRONG!  The order of map keys\/values is unpredictable.\n;; You may or may not get what you want.\nuser&gt; (println (gaka\/css [:a {:border 0 :border-left \"1px\"}]))\na {\n  border-left: 1px;\n  border: 0;}\n\n;; OK\nuser&gt; (println (gaka\/css [:a :border 0 :border-left \"1px\"]))\na {\n  border: 0;\n  border-left: 1px;}\n\n;; OK\nuser&gt; (println (gaka\/css [:a (list :border 0 :border-left \"1px\")]))\na {\n  border: 0;\n  border-left: 1px;}\n\n;; OK\nuser&gt; (println (gaka\/css [:a :border 0 {:border-left \"1px\"}]))\na {\n  border: 0;\n  border-left: 1px;}\n\n;; OK\nuser&gt; (println (gaka\/css [:a {:border 0} {:border-left \"1px\"}]))\na {\n  border: 0;\n  border-left: 1px;}\n<\/code><\/pre>\n<p>If you want a fancy selector or attribute that doesn\u2019t work as a keyword, use a string.<\/p>\n<pre><code>user&gt; (println (gaka\/css [\"input[type=text]\" :font-family \"\\\"Bitstream Vera Sans\\\", monospace\"]))\ninput[type=text] {\n  font-family: \"Bitstream Vera Sans\", monospace;}\n<\/code><\/pre>\n<p>If you want output suitable for inline CSS, use <code>inline-css<\/code>:<\/p>\n<pre><code>user&gt; (println (str \"foo\"))\nfoo\n<\/code><\/pre>\n<p>An easy way to compile your CSS to a file and make sure it\u2019s always up-to-date is to throw a <code>save-css<\/code> call at the bottom of your source file.<\/p>\n<pre><code>(ns my-site.css\n  (:require (gaka [core :as gaka)\n\n(def rules [...])\n\n(save-css \"public\/css\/style.css\" rules)\n<\/code><\/pre>\n<p>Now every time you re-compile this file (for example, <code>C-c C-k<\/code> in Slime\/Emacs), a static CSS file in <code>public\/css<\/code> will be generated or updated. This is the prefered way to serve CSS files for a web app (to avoid re-compiling your CSS on every request, which is probably pointless).<\/p>\n<p>That\u2019s about it.<\/p>\n<h2>Limitations<\/h2>\n<p>Gaka currently outputs less-than-optimal CSS under certain circumstances and errs on the side of verbosity to preserve correctness.<\/p>\n<p>Gaka doesn\u2019t validate your CSS or check your spelling.<\/p>\n<p>Gaka makes no attempt to be fast. You should compile your CSS, save it to a file and serve it statically.<\/p>\n<p>I wrote (most of) this in one afternoon while eating a tasty ham and turky sandwich. Bugs are likely.<\/p>\n<h2>Install<\/h2>\n<p>To fetch from CLojars (via Leiningen) put this in your project.clj:<\/p>\n<pre><code>[gaka \"0.3.0\"]\n<\/code><\/pre>\n<h2>License<\/h2>\n<p>Eclipse Public License 1.0, see http:\/\/opensource.org\/licenses\/eclipse-1.0.php.<\/p>\n<h2>Thanks<\/h2>\n<p>Steve Purcell for map syntax and inline CSS.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>by Brian Carper Gaka is a CSS-generating library for Clojure inspired partly by Sass and similar to Hiccup. Features Simple Indented output Selector nesting \u201cMixins\u201d Purpose CSS syntax is verbose, with lots of curly braces and semi-colons. Writing CSS as s-expressions is a way to ensure you have proper syntax, because the program handles the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-7629","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/7629","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/comments?post=7629"}],"version-history":[{"count":0,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/7629\/revisions"}],"wp:attachment":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/media?parent=7629"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/categories?post=7629"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/tags?post=7629"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}