Skip to content

Instantly share code, notes, and snippets.

@theironsamurai
Forked from noprompt/project.clj
Created June 19, 2014 22:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save theironsamurai/a0f6aa0b528fea1201ff to your computer and use it in GitHub Desktop.
Save theironsamurai/a0f6aa0b528fea1201ff to your computer and use it in GitHub Desktop.
(defproject semantic-gs "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.5.1"]
[compojure "1.1.5"]
[garden "0.1.0-beta5"]
[hiccup "1.0.3"]]
:plugins [[lein-ring "0.8.5"]]
:ring {:handler semantic-gs.handler/app}
:profiles
{:dev {:dependencies [[ring-mock "0.1.5"]]}})
(ns semantic-gs.handler
"Demonstration using Garden to recreate the Semantic Grid framework.
SEE: https://github.com/noprompt/garden
SEE: http://semantic.gs/
SEE: https://github.com/twigkit/semantic.gs/blob/master/stylesheets/scss/grid.scss"
(:use compojure.core)
(:refer-clojure :exclude [+ - * /])
(:require [compojure.handler :as handler]
[compojure.route :as route]
[garden.units :as gu]
[garden.color :as gc]
[garden.arithemetic :refer [+ - * /]]
[garden.def :refer [defrule]]
[garden.core :refer [css]]
[hiccup.page :refer [html5]]))
(def ^{:doc "The famous \"micro\" clearfix."}
clearfix
["&"
{:*zoom 1}
["&:before" "&:after"
{:content "\"\""
:display "table"}]
["&:after"
{:clear "both"}]])
;; In Sass configuration is achieved through the use of mutable
;; variables. In Clojure we can define our default configuration as an
;; immutable *value*. Later we can refer to this value when building
;; out or grid system. Note how we can use Clojure's tagged literals
;; to make our stylesheet code look more like CSS.
;; SEE: https://github.com/twigkit/semantic.gs/blob/master/stylesheets/scss/grid.scss#L6-L8
(def grid-defaults
{:column-width #px 60
:gutter-width #px 20
:columns 12})
;; This function has been ported directly from the SCSS code.
;; SEE: https://github.com/twigkit/semantic.gs/blob/master/stylesheets/scss/grid.scss#L11-L13
(defn grid-system-width
[{:keys [column-width columns gutter-width]}]
(+ (* column-width columns)
(* gutter-width columns)))
;; Since we aren't using mutable values to configure our grid system
;; and it's behavior, we need to apply a functional approach. Instead
;; of functions which simply respond to mutable values in the
;; surrounding code, we use higher order functions to create an
;; "instance" of a grid system based upon it's input values.
;; There are four key functions which are commonly used when working
;; with grid systems: `row`, `column`, `push`, and `pull`. The
;; following higher order functions assist in creating them. Each one
;; accepts grid configuration via a map and returns an instance of one
;; of these key functions.
;; https://github.com/twigkit/semantic.gs/blob/master/stylesheets/scss/grid.scss#L46-L53
(defn row-fn
"Creates a row function based on a grid configuration."
[{:keys [total-width grid-width gutter-width]}]
(fn []
["&"
clearfix
{:display "block"
:width (* total-width
(/ (+ gutter-width grid-width)
grid-width))
:margin [0 (* total-width
(- (/ (* 0.5 gutter-width)
grid-width)))]}]))
;; https://github.com/twigkit/semantic.gs/blob/master/stylesheets/scss/grid.scss#L54-L61
(defn column-fn
"Creates a column function based on a grid configuration."
[{:keys [total-width grid-width column-width gutter-width]}]
(fn [n]
{:display "inline"
:float "left"
:width (* total-width
(/ (- (* (+ gutter-width
column-width)
n)
gutter-width)
grid-width))
:margin [0 (* total-width
(/ (* 0.5 gutter-width)
grid-width))]}))
(defn offset-margin
"Computes the offset amount for push and pull functions."
[{:keys [total-width grid-width column-width gutter-width]} amount]
(+ (* total-width
(/ (* (+ gutter-width
column-width)
amount)
grid-width))
(* total-width
(/ (* 0.5 gutter-width)
grid-width))))
;; Lastly, we need a function for tying everything together.
(defn create-grid
([]
;; Make use of the default grid settings.
(create-grid grid-defaults))
([grid-opts]
(let [;; Ensure we have everything we need to generate the key
;; functions mentioned above.
opts (merge grid-defaults grid-opts)
{:keys [column-width gutter-width total-width]} opts
;; Calculate the grid with.
grid-width (grid-system-width grid-opts)
opts (assoc opts
:grid-width grid-width
:total-width (or total-width grid-width))
;; Partially evaluate `offset-margin` with our grid options
;; for our custom `push` and `pull` functions.
offset (partial offset-margin opts)]
;; Return a map representing an instance of our grid system.
{:row (row-fn opts)
:column (column-fn opts)
:push (fn [amount]
{:margin-left (offset amount)})
:pull (fn [amount]
{:margin-right (offset amount)})})))
;; With our grid factory in place let's put together a demo. For
;; demonstration purposes we'll take the easy route and build the
;; "fixed" grid (http://semantic.gs/examples/fixed/fixed.html).
;; I like to create this alias because it better expresses the intent
;; in the context of gardening.
(def styles list)
;; Define a few rule functions to make the code more expressive and
;; readable.
(defrule center :div.center)
(defrule top :section#top)
(defrule main :section#main)
(defrule sidebar :section#sidebar)
(defrule headings :h1 :h2 :h3)
;; I like this approach as well.
(def center-text {:text-align "center"})
;; Define our "fixed" grid CSS.
(def fixed
;; Create a standard grid and bind the key values.
(let [{:keys [row column push pull]} (create-grid)]
(styles
["*" "*:after" "*:before"
{:box-sizing "border-box"}]
[:body
{:width (gu/percent 100)
:font-family [["Georgia" :sans-serif]]
:padding 0
:margin 0}
clearfix]
(headings
{:font-weight "normal"})
(center
{:width #px 960
:margin [0 "auto"]
:overflow "hidden"})
(top
(column 12)
{:margin-bottom #em 1
:color #hsl [0 0 100]
:background #hsl [0 0 0]
:padding #px 20})
(main
center-text
(column 9)
{:color #hsl [0 0 40]
:background #hsl [0 0 80]
:padding #px 20})
(sidebar
center-text
(column 3)
{:color #hsl [0 0 40]
:background #hsl [0 0 80]
:padding #px 20}))))
(defroutes app-routes
(GET "/" []
;; Render the demo. Compare with the demo on the Semantic Grid
;; website.
;;
;; SEE: http://semantic.gs/examples/fixed/fixed.html
(html5
[:head
[:style (css fixed)]]
[:body
(center
(top [:h1 "The Semantic Grid System"])
(main [:h2 "Main"])
(sidebar [:h2 "Sidebar"]))]))
(route/resources "/")
(route/not-found "Not Found"))
(def app
(handler/site app-routes))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment