Skip to content

Instantly share code, notes, and snippets.

@stevedonovan
Created August 30, 2011 10:31
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevedonovan/1180617 to your computer and use it in GitHub Desktop.
Save stevedonovan/1180617 to your computer and use it in GitHub Desktop.
A little Lua DSL for generating CSS
--[[
A Lua module that defines a little DSL for generating CSS
-- csstest.lua
require 'css'
width = 500
lmargin = 50
leftm = 150
gap = 10
fore = '#FFFFFF'
back = '#000000'
left_fore = '#FFFFFF'
left_back = '#000000'
css.body {
width = width,
margin = {left=lmargin},
color = fore,
background_colour = back
}
css.id.left {
float = 'left',
width = leftm,
color = left_fore,
background_colour = left_back
}
css.id.content {
margin = {left=leftm+gap},
}
print(tostring(css)) -- could write to file, etc
]]
local function side_str (side)
if side then return '-'..side else return '' end
end
-- make numbers into pixel sizes
local function size_str (size)
if type(size) == 'number' then
return size..'px'
elseif type(size) == 'string' then
return size
else
error("size must be number or string")
end
end
-- has a table only got keys with names of sides?
local sides = {left=true,right=true,bottom=true,top=true}
function has_sides (t)
local ok = false
for k in pairs(t) do
if not sides[k] then return false end
ok = true
end
return ok
end
-- border
-- border = true
-- border = {width=3}
-- border = {left={color='#999'}}
function set_border (tbl,side,t)
t = t or {}
t.width = t.width or '1px'
t.style = t.style or 'solid'
t.color = t.color or '#0000'
side = side_str(side)
local border = 'border'..side..'-'
tbl[border..'width'] = t.width
tbl[border..'style'] = t.style
tbl[border..'color'] = t.color
end
function process_border (tbl,spec,side)
if spec == true then -- defaults
set_border(tbl,side)
elseif type(spec) == 'table' then
if not has_sides(spec) then
set_border(tbl,side,spec)
else
for side, sspec in pairs(spec) do
process_border(tbl,sspec,side)
end
end
end
end
-- margins or padding
-- margin = 4
-- margin = {left=2, right=4}
function process_margin_or_padding (tbl,which,spec,side)
if type(spec) ~= 'table' then
tbl[which..side_str(side)] = size_str(spec)
elseif has_sides(spec) then
for side, sspec in pairs(spec)do
process_margin_or_padding(tbl,which,sspec,side)
end
else
error('must contain only top, left, right and bottom keys')
end
end
function css_body (self,spec)
self:write '{\n'
local processed = {}
for prop, val in pairs(spec) do
if prop == 'border' then
process_border(processed,val)
elseif prop == 'margin' or prop == 'padding' then
process_margin_or_padding(processed,prop,val)
else
if type(val) == 'number' then
val = size_str(val)
end
processed[prop] = val
end
end
for prop, val in pairs(processed) do
prop = prop:gsub('_','-')
self:write('\t'..prop..': '..val..';\n')
end
self:write '}\n'
end
function css_ (self,selector)
if type(selector) == 'string' then
self:write(selector..' ')
return function(spec)
css_body(self,spec)
end
else
local spec = selector
local ss, append = {}, table.insert
for i,s in ipairs(self.ss) do
if s == 'id' then
append(ss,'#')
elseif s == 'class' then
append(ss,'.')
else
append(ss,s)
if self.ss[i+1] ~= 'class' then
append(ss,' ')
end
end
end
selector = table.concat(ss)
self.ss = {}
self:write(selector..' ')
css_body(self,spec)
end
end
css = {
ss={};
write = function(self,text) -- we are a rope!
self[#self+1] = text
end;
clear = function()
css.ss = {}
while table.remove(css) do end
end;
empty = function(val)
if not val or val == '' then return nil end
return val
end;
}
setmetatable(css,{
-- the Dot Builder pattern
__index = function(self,key)
local b = self.ss
b[#b+1] = key
return self
end;
__tostring = table.concat, -- we are a rope!
__call = css_
})
return css
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment