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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +171 -0
- data/README.md +27 -8
- data/lib/zine.rb +37 -77
- data/lib/zine/cli.rb +16 -0
- data/lib/zine/page.rb +8 -3
- data/lib/zine/post.rb +17 -6
- data/lib/zine/posts_and_headlines.rb +202 -0
- data/lib/zine/server.rb +36 -4
- data/lib/zine/skeleton/source/assets/webicon-rss-m.png +0 -0
- data/lib/zine/skeleton/source/assets/webicon-rss.svg +18 -0
- data/lib/zine/skeleton/source/assets/webicon-twitter-m.png +0 -0
- data/lib/zine/skeleton/source/assets/webicon-twitter.svg +24 -0
- data/lib/zine/skeleton/source/screen.css +1 -1
- data/lib/zine/skeleton/source/styles/screen.scss +291 -0
- data/lib/zine/skeleton/source/templates/footer_partial.erb +16 -4
- data/lib/zine/skeleton/source/templates/home.erb +3 -1
- data/lib/zine/skeleton/zine.yaml +38 -25
- data/lib/zine/style.rb +19 -0
- data/lib/zine/tag.rb +1 -0
- data/lib/zine/upload.rb +161 -0
- data/lib/zine/version.rb +1 -1
- data/lib/zine/watcher.rb +88 -0
- metadata +115 -2
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
|
-
|
22
|
-
|
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(
|
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
|
-
|
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
|
Binary file
|
@@ -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>
|
Binary file
|
@@ -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,
|
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
|
+
}
|