zine 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/zine/page.rb CHANGED
@@ -12,7 +12,7 @@ module Zine
12
12
  # A page on the site where the content comes from a file's markdown, and the
13
13
  # destination's location mirrors its own
14
14
  class Page
15
- attr_reader :formatted_data
15
+ attr_reader :dest_path, :formatted_data, :source_file, :template_bundle
16
16
  # the meta data, passed formatted to the template
17
17
  class FormattedData
18
18
  include ERB::Util
@@ -28,6 +28,7 @@ module Zine
28
28
  @page = { date_rfc3339: front_matter['date'],
29
29
  date_us: parse_date(front_matter['date']),
30
30
  github_name: site_opt['options']['github_name'],
31
+ links_array: site_opt['links'],
31
32
  num_items_on_home: site_opt['options']['num_items_on_home'],
32
33
  site_author: site_opt['options']['site_author'],
33
34
  site_description: site_opt['options']['site_description'],
@@ -59,8 +60,10 @@ module Zine
59
60
  :pageDateUS)
60
61
 
61
62
  def initialize(md_file_name, dest, templates, site_options)
63
+ @source_file = md_file_name
62
64
  file_parts = File.open(md_file_name, 'r').read.split('---')
63
- @formatted_data = FormattedData.new(parse_yaml(file_parts[1]),
65
+ @formatted_data = FormattedData.new(parse_yaml(file_parts[1],
66
+ md_file_name),
64
67
  site_options)
65
68
  @dest_path = dest
66
69
  @raw_text = file_parts[2]
@@ -71,6 +74,7 @@ module Zine
71
74
  @header_partial = templates.header
72
75
  @footer_partial = templates.footer
73
76
  @template = templates.body
77
+ @template_bundle = templates
74
78
  end
75
79
 
76
80
  def parse_markdown
@@ -81,9 +85,10 @@ module Zine
81
85
  smart_quotes: %w(apos apos quot quot),
82
86
  syntax_highlighter: 'rouge'
83
87
  ).to_html
88
+ @raw_text = nil
84
89
  end
85
90
 
86
- def parse_yaml(text)
91
+ def parse_yaml(text, md_file_name)
87
92
  YAML.safe_load text
88
93
  rescue Psych::Exception
89
94
  puts Rainbow("Could not parse front matter for: #{md_file_name}").red
data/lib/zine/post.rb CHANGED
@@ -4,8 +4,10 @@ module Zine
4
4
  # A post - content comes from the markdown, and the destination from the date
5
5
  class Post < Page
6
6
  def initialize(md_file_name, templates, site_options)
7
+ @source_file = md_file_name
7
8
  file_parts = File.open(md_file_name, 'r').read.split('---')
8
- @formatted_data = FormattedData.new(parse_yaml(file_parts[1]),
9
+ @formatted_data = FormattedData.new(parse_yaml(file_parts[1],
10
+ md_file_name),
9
11
  site_options)
10
12
  @raw_text = file_parts[2]
11
13
  init_templates(templates)
@@ -17,16 +19,25 @@ module Zine
17
19
  def make_path_from_date(build_dir)
18
20
  page_data = @formatted_data.page
19
21
  date = DateTime.parse(page_data[:date_rfc3339])
20
- dest_dir = File.join(build_dir,
21
- date.strftime('%Y'),
22
- date.strftime('%-m'))
23
- FileUtils.mkdir_p dest_dir
22
+ @dest_dir = File.join(build_dir,
23
+ date.strftime('%Y'),
24
+ date.strftime('%-m'))
24
25
  slg = Zine::Page.slug(page_data[:title]) + '.html'
25
- @dest_path = File.join(dest_dir, slg)
26
+ @dest_path = File.join(@dest_dir, slg)
26
27
  end
27
28
 
28
29
  def process
30
+ FileUtils.mkdir_p @dest_dir
29
31
  super
32
+ tag_and_uri_subprocess
33
+ end
34
+
35
+ def process_without_writing
36
+ parse_markdown
37
+ tag_and_uri_subprocess
38
+ end
39
+
40
+ def tag_and_uri_subprocess
30
41
  page_data = @formatted_data.page
31
42
  file_path = rel_path_from_build_dir(@dest_path).to_s
32
43
  @formatted_data.uri = URI.join(page_data[:site_URL], file_path).to_s
@@ -0,0 +1,202 @@
1
+ require 'zine/data_page'
2
+ require 'zine/post'
3
+ require 'zine/tag'
4
+
5
+ module Zine
6
+ # The blog posts and their associate headline files (the home page, an
7
+ # articles index, and RSS feed)
8
+ class PostsAndHeadlines
9
+ def initialize(site, options)
10
+ @options = options
11
+ @post_array = []
12
+ @site = site
13
+ @tags_by_post = []
14
+ dir = @options['directories']
15
+ @guard = Zine::Watcher.new self, dir['build'], dir['source']
16
+ @guard.start
17
+ read_post_markdown_files
18
+ sort_posts_by_date
19
+ end
20
+
21
+ def find_page_from_path(file_path)
22
+ post_to_rebuild = @post_array.detect do |post|
23
+ File.expand_path(post.source_file) == file_path
24
+ end
25
+ index = @post_array.find_index post_to_rebuild
26
+ { post: post_to_rebuild, index: index }
27
+ end
28
+
29
+ # The headlines pages - the home page, an articles index, and RSS feed
30
+ def headline_pages
31
+ dir = @options['directories']['build']
32
+ options = @options['options']
33
+ templates = @options['templates']
34
+ [{ build_dir: dir, name: 'articles', number: @post_array.size,
35
+ suffix: '.html', template_name: templates['articles'],
36
+ title: 'Articles' },
37
+ { build_dir: dir, name: 'index',
38
+ number: options['num_items_on_home'], suffix: '.html',
39
+ template_name: templates['home'], title: 'Home' },
40
+ { build_dir: dir, name: 'rss',
41
+ number: options['number_items_in_RSS'], suffix: '.xml',
42
+ template_name: templates['rss'], title: '' }]
43
+ end
44
+
45
+ def one_new_post(source_file)
46
+ post_name = @options['templates']['post']
47
+ @post_array << Zine::Post.new(source_file,
48
+ @site.make_template_bundle(post_name),
49
+ @options)
50
+ @tags_by_post << @post_array.last.process
51
+ # TODO: may need to reorder posts by date, and therefor redo tags
52
+ write_tags_and_headlines
53
+ end
54
+
55
+ # get build file from post or location, delete build, remove form post_array
56
+ def preview_delete(file_path)
57
+ page = find_page_from_path file_path
58
+ if page[:index].nil?
59
+ directories = @options['directories']
60
+ full = Pathname(file_path)
61
+ relative = full.relative_path_from(
62
+ Pathname(File.absolute_path(directories['source']))
63
+ )
64
+ relative_path = File.dirname(relative)
65
+ file = File.basename(relative, '.md') + '.html'
66
+ File.delete(File.join(directories['build'], relative_path, file))
67
+ else
68
+ File.delete(page[:post].dest_path)
69
+ @post_array.delete_at(page[:index])
70
+ end
71
+ end
72
+
73
+ def preview_rebuild(file_path)
74
+ page = find_page_from_path file_path
75
+ if page[:index].nil?
76
+ if File.dirname(file_path) ==
77
+ File.absolute_path(@options['directories']['posts'])
78
+ one_new_post file_path
79
+ else
80
+ rebuild_page file_path
81
+ end
82
+ else
83
+ rebuild_post page[:post], page[:index]
84
+ end
85
+ end
86
+
87
+ # the build folder equivalent of a non Markdown file in the source tree
88
+ # TODO: move from posts & headlines
89
+ def preview_relative_equivalent(file)
90
+ directories = @options['directories']
91
+ source_dir = Pathname(File.absolute_path(directories['source']))
92
+ build_dir = Pathname(File.absolute_path(directories['build']))
93
+ file_dir = Pathname(File.dirname(file))
94
+ File.join build_dir, file_dir.relative_path_from(source_dir)
95
+ end
96
+
97
+ # copy a non Markdown file, TODO: move form posts & headlines
98
+ def preview_straight_copy(file)
99
+ FileUtils.cp(file, preview_relative_equivalent(file))
100
+ end
101
+
102
+ # delete a non Markdown file, TODO: move form posts & headlines
103
+ def preview_straight_delete(file)
104
+ FileUtils.rm(File.join(
105
+ preview_relative_equivalent(file), File.basename(file)
106
+ ))
107
+ end
108
+
109
+ # rebuild a page that's not a post - doesn't create the file structure for a
110
+ # new file with new parent folders
111
+ def rebuild_page(file)
112
+ @site.write_markdown(@options['templates']['default'],
113
+ File.expand_path(@options['directories']['source']),
114
+ file)
115
+ end
116
+
117
+ # inserts the new post into the @post_array, builds the file, calls
118
+ # write_tags_and_headlines to rewrites the headline pages & tags
119
+ # RSS & home page will be redundant re-builds if not a recent page
120
+ def rebuild_post(post, index)
121
+ @post_array[index] = Zine::Post.new post.source_file,
122
+ post.template_bundle, @options
123
+ @tags_by_post[index] = @post_array[index].process
124
+ write_tags_and_headlines
125
+ # TODO: may need to reorder posts by date... means re-doing tags
126
+ end
127
+
128
+ # Read markdown files in the posts folder into an array of Posts
129
+ def read_post_markdown_files
130
+ file_name_array = Dir[File.join(@options['directories']['posts'], '*.md')]
131
+ post_name = @options['templates']['post']
132
+ file_name_array.each do |file|
133
+ @post_array << Zine::Post.new(file,
134
+ @site.make_template_bundle(post_name),
135
+ @options)
136
+ end
137
+ end
138
+
139
+ # Sort the Posts array into date order, newest first
140
+ def sort_posts_by_date
141
+ @post_array.sort_by! do |post|
142
+ post.formatted_data.page[:date_rfc3339]
143
+ end.reverse!
144
+ # TODO: .freeze -- currently modified during preview
145
+ end
146
+
147
+ # Process each of the headlines pages
148
+ def wrangle_headlines
149
+ headline_pages.each do |page|
150
+ write_headline page
151
+ end
152
+ end
153
+
154
+ # Write out the Posts, calls write_tags_and_headlines
155
+ def write
156
+ @tags_by_post = []
157
+ @post_array.each do |post|
158
+ @tags_by_post << post.process
159
+ end
160
+ write_tags_and_headlines
161
+
162
+ # end point
163
+ { posts: @post_array, guard: @guard }
164
+ end
165
+
166
+ # Generate data without writing files (for incremnetal builds & uploads)
167
+ def writeless
168
+ @tags_by_post = []
169
+ @post_array.each do |post|
170
+ @tags_by_post << post.process_without_writing
171
+ end
172
+
173
+ # end point
174
+ { posts: @post_array, guard: @guard }
175
+ end
176
+
177
+ # Pass headline data to the DataPage class to write the files
178
+ def write_headline(page)
179
+ data = page
180
+ data[:post_array] = []
181
+ @post_array.first(page[:number]).each do |post|
182
+ post_data = post.formatted_data
183
+ data[:post_array] << { page: post_data.page, html: post_data.html,
184
+ uri: post_data.uri }
185
+ end
186
+ data_page = DataPage.new(data,
187
+ @site.make_template_bundle(data[:template_name]),
188
+ @options, data[:suffix])
189
+ data_page.write
190
+ end
191
+
192
+ # Write out the tags and headline files
193
+ def write_tags_and_headlines
194
+ tag_name = @options['templates']['tag']
195
+ tag_index_name = @options['templates']['tag_index']
196
+ tags = Zine::Tag.new @tags_by_post, @site.make_template_bundle(tag_name),
197
+ @site.make_template_bundle(tag_index_name), @options
198
+ tags.write_tags
199
+ wrangle_headlines
200
+ end
201
+ end
202
+ end
data/lib/zine/server.rb CHANGED
@@ -1,18 +1,25 @@
1
+ require 'highline'
1
2
  require 'rainbow'
2
3
  require 'rack'
3
4
  require 'thin'
5
+ require 'zine/upload'
6
+ require 'zine/watcher'
4
7
 
5
8
  module Zine
6
9
  # Local preview web server
7
10
  class Server
8
- def initialize(root)
11
+ # def initialize(_posts, rel_path_build, _rel_path_source, upload_options,
12
+ def initialize(rel_path_build, upload_options, delete_array, upload_array)
13
+ @delete_array = delete_array
14
+ @upload_array = upload_array
15
+ root = File.absolute_path(rel_path_build)
9
16
  motd
10
- Thin::Server.start('127.0.0.1', 8080) do
17
+
18
+ @thin = Thin::Server.new('127.0.0.1', 8080) do
11
19
  use Rack::Static,
12
20
  urls: ['/'],
13
21
  index: 'index.html',
14
22
  root: root
15
-
16
23
  now = Time.now
17
24
  a_long_time = 100**4
18
25
  run lambda { |_env|
@@ -26,9 +33,34 @@ module Zine
26
33
  'Pragma' => 'no-cache',
27
34
  'Expires' => now - a_long_time
28
35
  },
29
- File.open(File.join(root, 'index.html'), File::RDONLY)]
36
+ [File.open(File.join(root, 'index.html'), File::RDONLY)]]
30
37
  }
31
38
  end
39
+ @thin.start
40
+
41
+ return if upload_options['method'] == 'none' ||
42
+ (@delete_array.empty? && @upload_array.empty?)
43
+ cli = HighLine.new
44
+ answer = cli.ask('Upload files? (Y/n)') { |q| q.default = 'Y' }
45
+ return if answer != 'Y'
46
+ file_upload rel_path_build, upload_options
47
+ end
48
+
49
+ # deploy
50
+ def file_upload(rel_path_build, upload_options)
51
+ puts Rainbow('Connecting...').green
52
+ begin
53
+ upload = Zine::Upload.new rel_path_build, upload_options,
54
+ # @guard.delete_array, @guard.upload_array
55
+ @delete_array, @upload_array
56
+ upload.delete
57
+ upload.deploy
58
+ rescue Errno::ENETUNREACH
59
+ puts Rainbow("Unable to connect to #{upload_options['host']}").red
60
+ rescue Net::SSH::AuthenticationFailed
61
+ puts Rainbow("Authentication failed for #{upload_options['host']}").red
62
+ puts 'Check your credential file, and maybe run ssh-add?'
63
+ end
32
64
  end
33
65
 
34
66
  def motd
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg version="1.1" id="Icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
4
+ <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="23.9995" y1="0" x2="23.9995" y2="48.0005">
5
+ <stop offset="0" style="stop-color:#F2A833"/>
6
+ <stop offset="1" style="stop-color:#E8621D"/>
7
+ </linearGradient>
8
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="url(#SVGID_1_)" d="M48,42c0,3.313-2.687,6-6,6H6c-3.313,0-6-2.687-6-6V6
9
+ c0-3.313,2.687-6,6-6h36c3.313,0,6,2.687,6,6V42z"/>
10
+ <path fill="#E8621D" d="M10.016,8.494c0,0,27.276-1.343,29.426,27.41h-5.509c0,0,1.076-20.959-23.917-22.841V8.494z"/>
11
+ <path fill="#E8621D" d="M10.016,18.034c0,0,15.99-0.538,19.08,17.869h-5.508c0,0-0.671-10.883-13.572-13.302V18.034z"/>
12
+ <path fill="#E8621D" d="M13.01,30.021c1.928,0,3.494,1.563,3.494,3.494c0,1.928-1.566,3.493-3.494,3.493
13
+ c-1.93,0-3.494-1.564-3.494-3.493C9.516,31.585,11.08,30.021,13.01,30.021z"/>
14
+ <path fill="#FFFFFF" d="M10.016,9.597c0,0,27.276-1.343,29.426,27.41h-5.509c0,0,1.076-20.96-23.917-22.842V9.597z"/>
15
+ <path fill="#FFFFFF" d="M10.016,19.138c0,0,15.99-0.539,19.08,17.869h-5.508c0,0-0.671-10.882-13.572-13.304V19.138z"/>
16
+ <path fill="#FFFFFF" d="M13.01,31.125c1.928,0,3.494,1.562,3.494,3.494c0,1.928-1.566,3.493-3.494,3.493
17
+ c-1.93,0-3.494-1.564-3.494-3.493C9.516,32.688,11.08,31.125,13.01,31.125z"/>
18
+ </svg>
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg version="1.1" id="Icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
4
+ <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="23.9995" y1="0" x2="23.9995" y2="48.0005">
5
+ <stop offset="0" style="stop-color:#4BD0EF"/>
6
+ <stop offset="1" style="stop-color:#29AAE1"/>
7
+ </linearGradient>
8
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="url(#SVGID_1_)" d="M48,42c0,3.313-2.687,6-6,6H6c-3.313,0-6-2.687-6-6V6
9
+ c0-3.313,2.687-6,6-6h36c3.313,0,6,2.687,6,6V42z"/>
10
+ <path fill="#29AAE1" d="M40.231,13.413c-1.12,0.497-2.323,0.833-3.588,0.984c1.291-0.774,2.28-1.998,2.747-3.457
11
+ c-1.206,0.716-2.543,1.236-3.968,1.516c-1.139-1.214-2.763-1.972-4.56-1.972c-3.449,0-6.246,2.796-6.246,6.247
12
+ c0,0.49,0.055,0.966,0.161,1.424c-5.192-0.261-9.795-2.749-12.876-6.528c-0.538,0.923-0.846,1.996-0.846,3.141
13
+ c0,2.167,1.103,4.08,2.779,5.199c-1.024-0.032-1.987-0.313-2.83-0.781c0,0.026,0,0.053,0,0.079c0,3.026,2.153,5.551,5.011,6.125
14
+ c-0.525,0.143-1.076,0.219-1.646,0.219c-0.403,0-0.794-0.038-1.176-0.11c0.795,2.48,3.102,4.287,5.835,4.338
15
+ c-2.138,1.675-4.832,2.675-7.758,2.675c-0.504,0-1.002-0.03-1.491-0.089c2.765,1.773,6.048,2.808,9.576,2.808
16
+ c11.49,0,17.774-9.519,17.774-17.774c0-0.271-0.006-0.54-0.019-0.809C38.334,15.766,39.394,14.666,40.231,13.413z"/>
17
+ <path fill="#FFFFFF" d="M40.231,14.739c-1.12,0.497-2.323,0.833-3.588,0.984c1.291-0.773,2.28-1.998,2.747-3.456
18
+ c-1.206,0.716-2.543,1.236-3.968,1.516c-1.139-1.214-2.763-1.972-4.56-1.972c-3.449,0-6.246,2.796-6.246,6.247
19
+ c0,0.489,0.055,0.966,0.161,1.424c-5.192-0.261-9.795-2.748-12.876-6.527c-0.538,0.923-0.846,1.996-0.846,3.141
20
+ c0,2.167,1.103,4.079,2.779,5.199c-1.024-0.032-1.987-0.313-2.83-0.781c0,0.026,0,0.052,0,0.079c0,3.027,2.153,5.551,5.011,6.125
21
+ c-0.525,0.144-1.076,0.219-1.646,0.219c-0.403,0-0.794-0.038-1.176-0.11c0.795,2.481,3.102,4.287,5.835,4.338
22
+ c-2.138,1.676-4.832,2.675-7.758,2.675c-0.504,0-1.002-0.03-1.491-0.089c2.765,1.773,6.048,2.808,9.576,2.808
23
+ c11.49,0,17.774-9.519,17.774-17.774c0-0.271-0.006-0.54-0.019-0.808C38.334,17.092,39.394,15.992,40.231,14.739z"/>
24
+ </svg>
@@ -1 +1 @@
1
- *{margin:0;padding:0}html,body{height:100%}body{background-color:#fff;font-family:GillSansRegular,'Gill Sans MT','Gill Sans','Century Gothic',Calibri,'Trebuchet MS',sans-serif;line-height:1.618;color:#333;text-align:center;font-weight:300}#skiptocontent{height:1px;width:1px;position:absolute;overflow:hidden;top:-10px}h1,h2,h3,h4,h5,h6{color:#333;letter-spacing:.1em}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color:#414f7c;text-decoration:none}h1 a:hover,h2 a:hover,h3 a:hover,h4 a:hover,h5 a:hover,h6 a:hover{color:#212721}h1{font-weight:300;font-size:2.618em;margin:1.809em 0 .809em}h2{font-weight:300;font-size:1.618em;margin:1.809em 0 .809em}h3,h4,h5,h6{font-weight:400;font-size:1em;margin:1.809em 0 .809em}p{margin:1em 0}a{color:#414f7c}a:hover{color:#212721}section{margin-bottom:1.618em}section>section{margin-bottom:3.236em}body>header{width:43em;text-align:center;margin:0 auto 0}body>header a{text-decoration:none;margin-left:.5em;margin-right:.5em}body>header,body>main,body>footer{display:block}body>header a{color:#414f7c}body>header a:hover{color:#212721}body>header a.extra{color:#414f7c;margin-left:1em}body>header a.extra:hover{color:#212721}body>header nav ul li{display:inline;list-style:none}.button{width:30px;height:30px;display:inline-block;background-size:100%;text-indent:-999em;text-align:left;margin:20px}.twitter{background:url('/assets/webicon-twitter-m.png');background-image:url('/assets/webicon-twitter.svg'),none}.rss{background:url('/assets/webicon-rss-m.png');background-image:url('/assets/webicon-rss.svg'),none}main{text-align:left;width:43em;margin:3em auto 2em}main li{margin-left:2.618em}.meta{color:#667}footer{width:43em;color:#667;border-top:4px solid #ddd;margin:3em auto 2em;overflow:hidden}footer .contact{float:left;margin-right:3em}footer .contact a,.tags a{color:#414f7c;text-decoration:none}footer .contact a:hover,.tags a:hover{color:#212721;text-decoration:none}.tags ul li{list-style:none;display:inline;font-variant:small-caps;font-size:1.2em}.archive a{text-decoration:none}ul.archive,ul.archive ul{margin-left:0}ul.archive li,ul.archive ul li{list-style:none;margin-left:0}.post pre{border:1px solid #ddd;background-color:#fff;padding:0 .4em}p.date{color:#667}pre{background-color:#eee;padding:1em;white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}code{font-family:Consolas,Menlo,Monaco,'Lucida Console','Courier New',monospace,serif;font-size:.8em}blockquote{margin:2em 2em 2em 1em;padding:0 .75em 0 1.25em;border-left:2px solid #ddd;border-right:0 solid #ddd}@media all and (max-width:736px){body>header,main,footer{width:86%;margin:0 auto 0;padding:12px 24px 12px}p{margin-bottom:2em}.button{width:50px;height:50px;margin:20px}}
1
+ *{margin:0;padding:0}html,body{height:100%}body{background-color:#fff;font-family:GillSansRegular,"Gill Sans MT","Gill Sans","Century Gothic",Calibri,"Trebuchet MS",sans-serif;line-height:1.618;color:#333;text-align:center;font-weight:300}#skiptocontent{height:1px;width:1px;position:absolute;overflow:hidden;top:-10px}h1,h2,h3,h4,h5,h6{color:#333;letter-spacing:0.1em}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color:#414f7c;text-decoration:none}h1 a:hover,h2 a:hover,h3 a:hover,h4 a:hover,h5 a:hover,h6 a:hover{color:#212721}h1{font-weight:300;font-size:2.618em;margin:1.809em 0 0.809em}h2{font-weight:300;font-size:1.618em;margin:1.809em 0 0.809em}h3,h4,h5,h6{font-weight:400;font-size:1em;margin:1.809em 0 0.809em}p,li{margin:1em 0}ul{margin:0}a{color:#414f7c}a:hover{color:#212721}section{margin-bottom:1.618em}section>section{margin-bottom:3.236em}body>header{width:43em;text-align:center;margin:0 auto 0}body>header a{text-decoration:none;margin-left:0.5em;margin-right:0.5em}body>header,body>main,body>footer{display:block}body>header a{color:#414f7c}body>header a:hover{color:#212721}body>header a.extra{color:#414f7c;margin-left:1em}body>header a.extra:hover{color:#212721}body>header nav ul li{display:inline;list-style:none}.button{width:30px;height:30px;display:inline-block;background-size:100%;text-indent:-999em;text-align:left;margin:20px}.twitter{background:url("/assets/webicon-twitter-m.png");background-image:url("/assets/webicon-twitter.svg"),none}.rss{background:url("/assets/webicon-rss-m.png");background-image:url("/assets/webicon-rss.svg"),none}main{text-align:left;width:43em;margin:3em auto 2em}main li{margin-left:2.618em}.meta{color:#667}footer{width:43em;color:#667;border-top:4px solid #ddd;margin:3em auto 2em;overflow:hidden}footer .column{float:left;width:33%;text-align:left}footer .column ul{list-style:none}footer .column a,.tags a{color:#414f7c;text-decoration:none}footer .column a:hover,.tags a:hover{color:#212721;text-decoration:none}.tags ul li{list-style:none;display:inline;font-variant:small-caps;font-size:1.2em}.archive a{text-decoration:none}ul.archive,ul.archive ul{margin-left:0}ul.archive li,ul.archive ul li{list-style:none;margin-left:0}.post pre{border:1px solid #ddd;background-color:#fff;padding:0 .4em}p.date{color:#667}pre{background-color:#eee;padding:1em;white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}code{font-family:Consolas,Menlo,Monaco,"Lucida Console","Courier New",monospace,serif;font-size:0.8em}blockquote{margin:2em 2em 2em 1em;padding:0 .75em 0 1.25em;border-left:2px solid #ddd;border-right:0px solid #ddd}@media all and (max-width: 736px){body>header,main,footer{width:86%;margin:0 auto 0;padding:12px 24px 12px}p{margin-bottom:2em}.button{width:50px;height:50px;margin:20px}footer .column{width:100%}}
@@ -0,0 +1,291 @@
1
+ $blue: #414f7c;
2
+ $darkGrey: #333;
3
+ $mediumGrey: #667;
4
+ $lightGrey: #ddd;
5
+ $black: #212721;
6
+ $white: #fff;
7
+ $offWhite: #eee;
8
+
9
+ $backgroundColour: $white;
10
+ $codeBackgroundColour: $offWhite;
11
+ $headingColour: $darkGrey;
12
+ $textColour: $darkGrey;
13
+ $linkColour: $blue;
14
+ $hoverColour: $black;
15
+ $metaAndFooterColour: $mediumGrey;
16
+ $borderColour: $lightGrey;
17
+
18
+ $desktopWidth: 43em;
19
+ $lightWeight: 300;
20
+ $heavyWeight: 400;
21
+
22
+ $regularFont: GillSansRegular, 'Gill Sans MT', 'Gill Sans', 'Century Gothic', Calibri, 'Trebuchet MS', sans-serif;
23
+ $monoFont: Consolas, Menlo, Monaco, 'Lucida Console', 'Courier New', monospace, serif;
24
+
25
+ * {
26
+ margin: 0;
27
+ padding: 0;
28
+ }
29
+
30
+ html, body { height: 100%; }
31
+
32
+ body {
33
+ background-color: $backgroundColour;
34
+ font-family: $regularFont;
35
+ line-height: 1.618;
36
+ color: $textColour;
37
+ text-align: center;
38
+ font-weight: $lightWeight;
39
+ }
40
+
41
+ #skiptocontent {
42
+ height: 1px;
43
+ width: 1px;
44
+ position: absolute;
45
+ overflow: hidden;
46
+ top: -10px;
47
+ }
48
+
49
+ h1,h2,h3,h4,h5,h6 {
50
+ color: $headingColour;
51
+ letter-spacing: 0.1em;
52
+ a {
53
+ color: $linkColour;
54
+ text-decoration: none;
55
+ }
56
+ a:hover {
57
+ color: $hoverColour;
58
+ }
59
+ }
60
+
61
+ h1 {
62
+ font-weight: $lightWeight;
63
+ font-size: 2.618em;
64
+ margin: 1.809em 0 0.809em;
65
+ }
66
+
67
+ h2 {
68
+ font-weight: $lightWeight;
69
+ font-size: 1.618em;
70
+ margin: 1.809em 0 0.809em;
71
+ }
72
+
73
+ h3, h4, h5, h6 {
74
+ font-weight: $heavyWeight;
75
+ font-size: 1em;
76
+ margin: 1.809em 0 0.809em;
77
+ }
78
+
79
+ p, li {
80
+ margin: 1em 0;
81
+ }
82
+
83
+ ul {
84
+ margin: 0;
85
+ }
86
+
87
+ a { color:$linkColour; }
88
+ a:hover { color: $hoverColour; }
89
+
90
+ /* Home */
91
+ section {
92
+ margin-bottom: 1.618em;
93
+ }
94
+
95
+ section > section {
96
+ margin-bottom: 3.236em;
97
+ }
98
+
99
+ /* Site */
100
+ body > header {
101
+ width: $desktopWidth;
102
+ text-align: center;
103
+ margin: 0 auto 0;
104
+ }
105
+
106
+ body > header a {
107
+ text-decoration: none;
108
+ margin-left: 0.5em;
109
+ margin-right: 0.5em;
110
+ }
111
+
112
+ body > header, body > main, body > footer {
113
+ display: block;
114
+ }
115
+
116
+ body > header a {
117
+ color: $linkColour;
118
+ }
119
+
120
+ body > header a:hover {
121
+ color: $hoverColour;
122
+ }
123
+
124
+ body > header a.extra {
125
+ color: $linkColour;
126
+ margin-left: 1em;
127
+ }
128
+
129
+ body > header a.extra:hover {
130
+ color: $hoverColour;
131
+ }
132
+
133
+ body > header nav ul li {
134
+ display: inline;
135
+ list-style: none;
136
+ }
137
+
138
+ .button {
139
+ width: 30px;
140
+ height: 30px;
141
+ display: inline-block;
142
+ background-size: 100%;
143
+ text-indent: -999em;
144
+ text-align: left;
145
+ margin: 20px;
146
+ }
147
+
148
+ .twitter {
149
+ background: url('/assets/webicon-twitter-m.png');
150
+ background-image: url('/assets/webicon-twitter.svg'), none;
151
+ }
152
+
153
+ .rss {
154
+ background: url('/assets/webicon-rss-m.png');
155
+ background-image: url('/assets/webicon-rss.svg'), none;
156
+ }
157
+
158
+ main {
159
+ text-align: left;
160
+ width: $desktopWidth;
161
+ margin: 3em auto 2em;
162
+ }
163
+
164
+ main li {
165
+ margin-left: 2.618em;
166
+ }
167
+
168
+ .meta {
169
+ color: $metaAndFooterColour;
170
+ }
171
+
172
+ footer {
173
+ width: $desktopWidth;
174
+ color: $metaAndFooterColour;
175
+ border-top: 4px solid $borderColour;
176
+ margin: 3em auto 2em;
177
+ overflow: hidden;
178
+ }
179
+
180
+ footer .column {
181
+ float: left;
182
+ width: 33%;
183
+ text-align: left;
184
+ }
185
+
186
+ footer .column ul { list-style: none; }
187
+ footer .column a, .tags a { color:$linkColour; text-decoration: none; }
188
+ footer .column a:hover, .tags a:hover { color:$hoverColour; text-decoration: none; }
189
+
190
+ .tags ul li {
191
+ list-style: none;
192
+ display: inline;
193
+ font-variant: small-caps;
194
+ font-size:1.2em;
195
+ }
196
+
197
+ .archive a {
198
+ text-decoration: none;
199
+ }
200
+
201
+ ul.archive, ul.archive ul {
202
+ margin-left: 0;
203
+ }
204
+
205
+ ul.archive li, ul.archive ul li {
206
+ list-style: none;
207
+ margin-left: 0;
208
+ }
209
+
210
+ /*footer .rss {
211
+ margin-top: 1.1em;
212
+ margin-right: -.2em;
213
+ float: right;
214
+ }
215
+
216
+ footer .rss img {
217
+ border: 0;
218
+ }*/
219
+
220
+ /* Posts */
221
+
222
+ /* standard */
223
+ .post pre {
224
+ border: 1px solid $borderColour;
225
+ background-color: $backgroundColour;
226
+ padding: 0 .4em;
227
+ }
228
+
229
+
230
+ p.date {
231
+ color: $mediumGrey;
232
+ }
233
+
234
+ /*.post ul, .post ol {
235
+ margin-left: 1.35em;
236
+ }*/
237
+
238
+ pre {
239
+ background-color: $codeBackgroundColour;
240
+ padding: 1em;
241
+ white-space: pre-wrap;
242
+ /* why necessary */
243
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
244
+ white-space: -pre-wrap; /* Opera 4-6 */
245
+ white-space: -o-pre-wrap; /* Opera 7 */
246
+ word-wrap: break-word;
247
+ }
248
+
249
+ code {
250
+ font-family: $monoFont;
251
+ font-size: 0.8em;
252
+ }
253
+
254
+ /* terminal
255
+ .post pre.terminal {
256
+ border: 1px solid $borderColour;
257
+ background-color: $codeBackgroundColour;
258
+ color: $textColour;
259
+ }
260
+
261
+ .post pre.terminal code {
262
+ background-color: $codeBackgroundColour;
263
+ }*/
264
+
265
+ /* quotes */
266
+ blockquote {
267
+ margin: 2em 2em 2em 1em;
268
+ padding: 0 .75em 0 1.25em;
269
+ border-left: 2px solid $borderColour;
270
+ border-right: 0px solid $borderColour;
271
+ }
272
+
273
+ @media all and (max-width: 736px) {
274
+ /* was max-device-width: 1242px */
275
+ body > header, main, footer {
276
+ width:86%;
277
+ margin: 0 auto 0;
278
+ padding: 12px 24px 12px;
279
+ }
280
+ p {
281
+ margin-bottom:2em;
282
+ }
283
+ .button {
284
+ width: 50px;
285
+ height: 50px;
286
+ margin: 20px;
287
+ }
288
+ footer .column {
289
+ width: 100%;
290
+ }
291
+ }