1
0
Fork 0

Initial public commit

master
Lukas Bestle 8 years ago
commit f9c5f3db5a

1
.gitignore vendored

@ -0,0 +1 @@
.DS_Store

@ -0,0 +1,54 @@
<div class="preview-canvas">
<h1>Color previews</h1>
<ul class="palette">
<li class="palette__color">
<div class="palette__swatch background--pagebg"></div>
<p class="palette__label">pagebg</p>
</li>
<li class="palette__color">
<div class="palette__swatch background--pagepattern"></div>
<p class="palette__label">pagepattern</p>
</li>
<li class="palette__color">
<div class="palette__swatch background--text"></div>
<p class="palette__label">text</p>
</li>
<li class="palette__color">
<div class="palette__swatch background--warn"></div>
<p class="palette__label">warn</p>
</li>
</ul>
<ul class="palette">
<li class="palette__color">
<div class="palette__swatch background--passive-light"></div>
<p class="palette__label">passive-light</p>
</li>
<li class="palette__color">
<div class="palette__swatch background--passive"></div>
<p class="palette__label">passive</p>
</li>
<li class="palette__color">
<div class="palette__swatch background--passive-dark"></div>
<p class="palette__label">passive-dark</p>
</li>
</ul>
<ul class="palette">
<li class="palette__color">
<div class="palette__swatch background--brand-primary"></div>
<p class="palette__label">brand-primary</p>
</li>
<li class="palette__color">
<div class="palette__swatch background--brand-primary-alternate"></div>
<p class="palette__label">brand-primary-alternate</p>
</li>
<li class="palette__color">
<div class="palette__swatch background--brand-secondary"></div>
<p class="palette__label">brand-secondary</p>
</li>
<li class="palette__color">
<div class="palette__swatch background--brand-secondary-alternate"></div>
<p class="palette__label">brand-secondary-alternate</p>
</li>
</ul>
</div>

@ -0,0 +1,4 @@
$color--brand-primary: #ed8f1b !default;
$color--brand-primary-alternate: lighten($color--brand-primary, 15%) !default;
$color--brand-secondary: #0bb888 !default;
$color--brand-secondary-alternate: lighten($color--brand-secondary, 5%) !default;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

@ -0,0 +1,41 @@
<div class="contact">
<div class="contact__main">
<form class="contact__form form" action="" method="post">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<img class="contact__stamp" src="<?= url('assets/img/stamp.svg') ?>" alt="">
</form>
<div class="contact__thank-you">
Thank you for your message.
</div>
</div>
<aside class="contact__sidebar">
<div class="contact__detail">
<h2 class="contact__heading">Email</h2>
<div class="contact__email"><?= str::email('lukas@example.com') ?> <a href="<?= url('legal') ?>#contact">?</a></div>
<small><a href="<?= url('pgp.asc') ?>" rel="pgpkey">PGP: 2E2A DD32 FE50 61C1</a></small>
</div>
<div class="contact__detail">
<h2 class="contact__heading">Twitter</h2>
<?= twitter('lukasbestle') ?>
</div>
</aside>
</div>
<script>
// Demonstration of the form animation
setTimeout(function() {
document.querySelector('.form').classList.add('form--sent');
}, 2000);
setTimeout(function() {
document.querySelector('.form').classList.add('form--success');
}, 2750);
</script>

@ -0,0 +1,100 @@
.contact {
position: relative;
display: flex;
justify-content: space-between;
@media (max-width: 50em) {
flex-direction: column;
}
}
.contact__main {
flex: 1;
}
.contact__form {
position: relative;
}
.contact__stamp {
position: absolute;
bottom: -1.5rem;
right: 1rem;
width: 6rem;
visibility: hidden;
opacity: 0;
filter: blur(2px);
transform: scale(1.3);
transition: visibility 0s linear 0.3s, opacity 0.3s ease, filter 0.3s ease, transform 0.3s ease;
.form--sent & {
visibility: visible;
opacity: 1;
filter: none;
transform: none;
transition-delay: 0s;
}
}
.contact__thank-you {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: -1;
}
.contact__sidebar {
max-width: 16rem;
margin-left: 2rem;
@media (max-width: 50em) {
max-width: 100%;
margin: 0;
margin-top: 2rem;
display: flex;
flex-wrap: wrap;
}
}
.contact__detail {
@media (min-width: 50em), (max-width: 42em) {
&:not(:last-child) {
margin-bottom: 1.8rem;
}
}
@media (max-width: 50em) {
width: 50%;
}
@media (max-width: 42em) {
width: 100%;
}
}
.contact__heading {
font-size: -cdd-rem(20);
margin-bottom: 0.3em;
}
.contact__email {
display: flex;
margin-bottom: 0.3rem;
> :nth-child(1) {
margin-right: 0.5rem;
padding-right: 0.5rem;
border-right: 1px solid $color--passive;
}
> :nth-child(2) {
align-self: center;
font-size: -cdd-rem(22);
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.0 KiB

@ -0,0 +1,14 @@
<?php
return [
'defaults' => [
'illustration' => false,
'text' => '<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptas repellendus modi ea praesentium totam, earum, accusamus pariatur cumque.</p>'
],
'preview' => function() {
// Use the home illustration if we have one
$illustration = page('home')->image('illustration.svg')->url();
return compact('illustration');
}
];

@ -0,0 +1,21 @@
<article class="home-banner">
<?php if($illustration): ?>
<img class="home-banner__illustration" src="<?= $illustration ?>" alt="">
<?php endif ?>
<div class="home-banner__description">
<div class="home-banner__text text"><?= $text ?></div>
<?php pattern('shared/2-blocks/button', [
'text' => l::get('home-banner.learnmore', 'Learn more'),
'label' => l::get('home-banner.learnmore.label', 'Learn more about my services'),
'link' => url('services'),
'modifiers' => 'novisited'
]) ?>
<?php pattern('shared/2-blocks/button', [
'text' => l::get('home-banner.contact', 'Contact'),
'link' => url('contact'),
'modifiers' => ['novisited', 'secondary']
]) ?>
</div>
</article>

@ -0,0 +1,36 @@
.home-banner {
display: flex;
align-items: flex-start;
@media (max-width: 44em) {
flex-direction: column;
align-items: center;
}
}
.home-banner__illustration {
width: 25em;
max-width: 100%;
margin-right: 4em;
@media (max-width: 60em) {
width: 45%;
margin-right: 2em;
}
@media (max-width: 44em) {
width: 20em;
margin-right: 0;
margin-bottom: 1em;
}
}
.home-banner__description {
flex: 1;
}
.home-banner__text {
font-size: -cdd-rem(24);
line-height: 1.55;
margin-bottom: 0.5em;
}

@ -0,0 +1,3 @@
<a href="<?= site()->homepage()->url() ?>" class="logo" title="Homepage">
<?= f::read(__DIR__ . DS . 'logo.svg') ?>
</a>

@ -0,0 +1,50 @@
.logo__gear {
transition: transform 2s ease-in-out 2.3s;
.logo:hover &, .logo:focus & {
transition-delay: 0.7s;
}
}
.logo__gear--left {
transform-origin: 261px 261px;
.logo:hover &, .logo:focus & {
transform: rotate(-90deg);
}
}
.logo__gear--right {
transform-origin: 1100px 340px;
.logo:hover &, .logo:focus & {
transform: rotate(90deg);
}
}
.logo__esignd, .logo__gears {
opacity: 1;
transition: opacity 1s ease-in-out 1.4s;
.logo:hover &, .logo:focus & {
opacity: 0;
transition-delay: 2.7s;
}
}
.logo__leftd {
transition: transform 1s ease-in-out 0.7s;
.logo:hover &, .logo:focus & {
transform: translateX(-470px);
transition-delay: 3.4s;
}
}
.logo__tilde {
opacity: 0;
transition: opacity 1s ease-in-out;
.logo:hover &, .logo:focus & {
opacity: 1;
transition-delay: 4.3s;
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 22 KiB

@ -0,0 +1,17 @@
<?php
return [
'defaults' => [
'illustration' => false,
'title' => 'Lorem ipsum dolor',
'coloredTitle' => true,
'text' => '<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptas repellendus modi ea praesentium totam, earum, accusamus pariatur cumque.</p>'
],
'preview' => function() {
// Use a random service illustration if we have one
$page = page('services');
$illustration = ($page)? $page->images()->shuffle()->first()->url() : false;
return compact('illustration');
}
];

@ -0,0 +1,10 @@
<section class="service illustrated-text illustrated-text--decorative text">
<?php if($illustration): ?>
<img class="illustrated-text__sidebar" src="<?= $illustration ?>" alt="">
<?php endif ?>
<div class="service__description illustrated-text__main">
<h2 class="service__title<?php e($coloredTitle, ' service__title--colored') ?>"><?= html($title) ?></h2>
<?= $text ?>
</div>
</section>

@ -0,0 +1,24 @@
.service {
// Make the illustrations overlap
&:not(:first-of-type) {
margin-top: -0.5em;
@media (max-width: 50em) {
margin-top: -0.25em;
}
}
}
.service__description {
.service:not(:last-of-type) & {
margin-bottom: 2em;
}
:last-child {
margin-bottom: 0;
}
}
.service__title--colored::first-letter {
color: $color--brand-secondary;
}

@ -0,0 +1,7 @@
(function(app) {
'use strict';
app.on('ajax.after', function(target, data) {
document.querySelector('.nav').innerHTML = data.nav;
});
})(window.app = window.app || {});

@ -0,0 +1,70 @@
//=require element-closest/closest.js
//=require whatwg-fetch/fetch.js
(function(app) {
'use strict';
// Listen on submit events on body (events from forms will bubble up)
document.body.addEventListener('submit', function(e) {
// Find closest form parent
var form = e.target.closest('.contact__form');
// Only continue if the form was found and if we have Promise support
if(!form || !Promise) return;
e.preventDefault();
// Disable the submit button and display the animated stamp
console.info('Sending form data');
form._submit.disabled = true;
form.classList.add('form--sent');
// Get form data
// Explicitly add value of submit button (browser bug in Chrome/FF?)
var data = new FormData(form);
data.append('_submit', form._submit.value);
// Send the form data via AJAX
var options = {
method: 'POST',
body: data,
credentials: 'same-origin',
headers: {'Accept': 'application/json'}
};
var dataPromise = fetch(form.action, options)
.then(app.validateHttpStatus)
.then(app.parseJsonResponse);
// Wait for the stamp animation
Promise.all([dataPromise, app.timerPromise(750)])
.then(function(values) {
// Get the result of the data promise
var data = values[0];
// Mark the (in)valid fields
for(var field in data.fields) {
// Add the class if value is true, otherwise remove it
form[field].classList.toggle('input--invalid', data.fields[field]);
}
// Check if the form submission was successful
if(data.success) {
form.querySelector('.form__message').innerText = '';
document.querySelector('.contact__thank-you').innerText = data.message;
form.classList.add('form--success');
} else {
// Throw message to catch it below, because apparently our code is a dog
throw data.message;
}
})
.catch(function(err) {
console.error(err);
// Display the error to the user
form.classList.remove('form--sent');
form.querySelector('.form__message').innerText = err;
// Enable the submit button again
form._submit.disabled = false;
});
});
})(window.app = window.app || {});

@ -0,0 +1,11 @@
// Import site-specific color variables
@import "1-globals/colors/colors";
// Import shared styles
@import "../shared/shared";
// Import blocks in alphabetical order (they are strictly independent)
@import "2-blocks/contact/contact";
@import "2-blocks/home-banner/home-banner";
@import "2-blocks/logo/logo";
@import "2-blocks/service/service";

@ -0,0 +1,21 @@
<div class="preview-canvas text">
<h1>Code highlighting</h1>
<p>Lorem ipsum dolor sit amet, <code>$page->text()->kt()</code> elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<pre><code class="language-js"><?= f::read(kirby()->roots()->index() . DS . 'gulpfile.js') ?></code></pre>
<p>Lorem ipsum dolor sit amet, <code>$page->text()->kt()</code> elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco <kbd>&#8984; Cmd</kbd> + <kbd>&#8679; Shift</kbd> + <kbd>3</kbd>
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<pre><code class="language-markdown"><?= f::read(kirby()->roots()->index() . DS . 'readme.md') ?></code></pre>
</div>

@ -0,0 +1,15 @@
//=require prismjs/prism.js
//=require prismjs/plugins/autoloader/prism-autoloader.js
(function(app) {
'use strict';
// Set autoloading path of the language definitions
Prism.plugins.autoloader.languages_path = '/assets/js/prism/';
// Re-run Prism.js on page change
app.on('ajax.after', function(target, data) {
console.info('Highlighting code blocks on new page');
Prism.highlightAll();
});
})(window.app = window.app || {});

@ -0,0 +1,120 @@
// @font-face definitions
@include -cdd-font-face('Input Mono Narrow', 'input-mono-narrow-light', 300, normal);
pre, code, kbd {
font-family: "Input Mono Narrow", Consolas, Monaco, "Andale Mono", monospace;
font-weight: 300;
font-size: -cdd-rem(16);
word-spacing: 0;
}
pre {
tab-size: 4;
hyphens: none;
line-height: 1.5;
padding: 1.5em;
border-radius: 0.3em;
overflow: auto;
max-height: 30em;
margin-bottom: 1em;
}
:not(pre) > code, kbd {
padding: 0.2em 0.3em 0.1em;
border-radius: 0.3em;
white-space: normal;
}
kbd {
padding: 0.4em 0.5em 0.15em;
background: $color--passive;
box-shadow: 0 2px 0 $color--passive-dark;
}
/**
* okaidia theme for JavaScript, CSS and HTML
* Loosely based on Monokai textmate theme by http://www.monokai.nl/
* @author ocodia
*
* Stripped from layout styling and other font styles
*/
code,
pre {
color: #f8f8f2;
background: none;
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
}
:not(pre) > code,
pre {
background: #272822;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #f8f8f2;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.constant,
.token.symbol,
.token.deleted {
color: #f92672;
}
.token.boolean,
.token.number {
color: #ae81ff;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #a6e22e;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: #f8f8f2;
}
.token.atrule,
.token.attr-value,
.token.function {
color: #e6db74;
}
.token.keyword {
color: #66d9ef;
}
.token.regex,
.token.important {
color: #fd971f;
}
.token.entity {
cursor: help;
}

@ -0,0 +1,5 @@
// Import shared styles
@import "../shared/shared";
// Import globals in correct order
@import "1-globals/typo-code/typo-code";

@ -0,0 +1,3 @@
# etc patterns
This directory contains patterns that have been prepared but are currently not being used for any site.

@ -0,0 +1,23 @@
# License for codesignd Patterns
## Webfonts
This repository contains webfont files of the [Input typeface](https://djr.com/input/). You may not use, copy, modify, merge, publish, distribute, sublicense and/or sell copies of these files if you don't have a valid font license (and even then you may only use the files that get provided to you directly).
Please support the work of David Jonathan Ross and buy a license if you want to use Input for your projects. You can find the [Input license terms](https://djr.com/license/) on David's website.
## Design, CSS code and graphics
Copyright (c) 2016 Lukas Bestle
You may not use, copy, modify, merge, publish, distribute, sublicense and/or sell copies of the design, CSS code and graphics from this project without explicit permission by Lukas Bestle.
## PHP and JS code
**The MIT License (MIT)**
Copyright (c) 2016 Lukas Bestle
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,11 @@
{
"name": "codesignd-patterns",
"description": "Pattern library for codesignd websites",
"homepage": "https://git.codesignd.com/sites/patterns",
"license": "SEE LICENSE IN license.md",
"private": true,
"copyright": "2016 Lukas Bestle",
"licenseCSS": "See https://git.codesignd.com/sites/patterns/blob/master/license.md",
"licenseJS": "MIT"
}

@ -0,0 +1,23 @@
# codesignd Patterns
Welcome to the pattern library that powers the codesignd websites!
Feel free to check out the building blocks I use and the code behind them.
## Take a look at the full source code
The source code of my sites is [hosted on my GitLab server](https://git.codesignd.com/groups/sites). Feel free to check that out too!
## Contact me
If you have any questions or if you would like to know more about the patterns, my sites or anything else, please don't hesitate to contact me:
- [Email and contact form](https://codesignd.com/contact)
- [Follow me on Twitter](https://twitter.com/lukasbestle)
- Find me in the [Kirby Forum](https://forum.getkirby.com/users/lukasbestle)
## License
Design, CSS code and graphics: © 2016 Lukas Bestle
PHP and JS code: MIT License
Full license terms in the [code repository](https://git.codesignd.com/sites/patterns/blob/master/license.md).

@ -0,0 +1,38 @@
<div class="preview-canvas">
<h1>Color previews</h1>
<ul class="palette">
<li class="palette__color">
<div class="palette__swatch background--pagebg"></div>
<p class="palette__label">pagebg</p>
</li>
<li class="palette__color">
<div class="palette__swatch background--pagepattern"></div>
<p class="palette__label">pagepattern</p>
</li>
<li class="palette__color">
<div class="palette__swatch background--text"></div>
<p class="palette__label">text</p>
</li>
<li class="palette__color">
<div class="palette__swatch background--warn"></div>
<p class="palette__label">warn</p>
</li>
</ul>
<ul class="palette">
<li class="palette__color">
<div class="palette__swatch background--passive-light"></div>
<p class="palette__label">passive-light</p>
</li>
<li class="palette__color">
<div class="palette__swatch background--passive"></div>
<p class="palette__label">passive</p>
</li>
<li class="palette__color">
<div class="palette__swatch background--passive-dark"></div>
<p class="palette__label">passive-dark</p>
</li>
</ul>
<p>Brand colors are specific to each site, so please also see the site-specific color previews.</p>
</div>

@ -0,0 +1,104 @@
$color--pagebg: #f2f2f2 !default;
$color--text: #000 !default;
$color--warn: #e63a16 !default;
// Passive colors include some primary brand color
$color--passive: mix($color--brand-primary, #ddd, 3%) !default;
$color--passive-dark: darken($color--passive, 10%) !default;
$color--passive-light: lighten($color--passive, 5%) !default;
// Mixin for the page pattern background
@mixin background--pagepattern {
background-color: $color--pagebg;
background: -cdd-img-url('pagepattern.svg'), none;
}
// Color helper classes
.color--pagebg {
color: $color--pagebg;
}
.background--pagebg {
background: $color--pagebg;
}
.background--pagepattern {
@include background--pagepattern;
}
.color--text {
color: $color--text;
}
.background--text {
background: $color--text;
}
.color--warn {
color: $color--warn;
}
.background--warn {
background: $color--warn;
}
.color--passive {
color: $color--passive;
}
.background--passive {
background: $color--passive;
}
.color--passive-dark {
color: $color--passive-dark;
}
.background--passive-dark {
background: $color--passive-dark;
}
.color--passive-light {
color: $color--passive-light;
}
.background--passive-light {
background: $color--passive-light;
}
.color--brand-primary {
color: $color--brand-primary;
}
.background--brand-primary {
background: $color--brand-primary;
}
.color--brand-primary-alternate {
color: $color--brand-primary-alternate;
}
.background--brand-primary-alternate {
background: $color--brand-primary-alternate;
}
.color--brand-secondary {
color: $color--brand-secondary;
}
.background--brand-secondary {
background: $color--brand-secondary;
}
.color--brand-secondary-alternate {
color: $color--brand-secondary-alternate;
}
.background--brand-secondary-alternate {
background: $color--brand-secondary-alternate;
}
// Set the main colors on body
body {
color: $color--text;
@include background--pagepattern;
@media print {
background: none;
}
}
// Selection background
::selection {
background: lighten($color--brand-primary, 30%);
}

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
<filter id="f" color-interpolation-filters="sRGB">
<feTurbulence type="fractalNoise" baseFrequency="0.2" numOctaves="5" result="noise"/>
<feDiffuseLighting in="noise" lighting-color="white" diffuseConstant="1" surfaceScale="0.3">
<feDistantLight azimuth="90" elevation="50"/>
</feDiffuseLighting>
</filter>
<rect width="500" height="500" filter="url(#f)" fill="#f2f2f2" opacity="0.2"/>
</svg>

After

Width:  |  Height:  |  Size: 471 B

@ -0,0 +1,89 @@
(function(app) {
'use strict';
// Store for the event handlers
var handlers = {};
var hasOwnProperty = handlers.hasOwnProperty;
/**
* Registers an event handler for a specified event
*
* @param {string} event Event name
* @param {Function} callback
* @return {object} Object with a remove() method to unsubscribe from events
*/
app.on = function(event, callback) {
// Create event array if not already existing
if(!hasOwnProperty.call(handlers, event)) handlers[event] = [];
// Add the callback to the array
var index = handlers[event].push(callback) - 1;
// Return function to delete handler again
return {
remove: function() {
delete handlers[event][index];
}
};
};
/**
* Triggers an event with a given event argument
*
* @param {string} event Event name
* @param {mixed} ... Arbitrary arguments to pass to every callback
*/
app.emit = function(event) {
if(!hasOwnProperty.call(handlers, event)) return;
var args = Array.prototype.slice.call(arguments, 1);
handlers[event].forEach(function(handler) {
handler.apply(undefined, args);
});
};
/**
* Returns a promise that resolves after the specified time
*
* @param {integer} time Time in milliseconds
* @return {Promise}
*/
app.timerPromise = function(time) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, time);
});
};
/**
* Validates that a fetch() request has a HTTP status code in the 200 range
*
* @param {Response} response fetch() response
* @return {Response} Same response object to allow for chaining
*/
app.validateHttpStatus = function(response) {
if(response.status >= 200 && response.status < 300) {
return response;
} else {
var error = new Error('HTTP response with status: ' + response.status + ' ' + response.statusText);
error.response = response;
throw error;
}
};
/**
* Converts a fetch() response to JSON
*
* @param {Response} response fetch() response
* @return {Promise}
*/
app.parseJsonResponse = function(response) {
// Validate that the response is JSON
var contentType = response.headers.get('Content-Type');
if(contentType.indexOf('application/json') !== 0) {
throw new Error('Got content type "' + contentType + '", expected JSON.');
}
// Convert response to JSON
return response.json();
};
})(window.app = window.app || {});

@ -0,0 +1,50 @@
///
// Converts a pixel value to ems with a base font size of 18 px
//
// @param {int} $px Target pixel value
// @return {value} Pixel value converted to ems
///
@function -cdd-em($px) {
@return ($px / 18) * 1em;
}
///
// Converts a pixel value to rems with a base font size of 18 px
//
// @param {int} $px Target pixel value
// @return {value} Pixel value converted to rems
///
@function -cdd-rem($px) {
@return ($px / 18) * 1rem;
}
///
// Returns the correct url() to an image in assets/img
//
// @param {string} $name Filename of the image
// @return {value}
///
@function -cdd-img-url($name) {
@return url(-cdd-cachebuster('/assets/img/' + $name));
}
///
// Helper to embed @font-face declarations
// Inspired by https://gist.github.com/jonathantneal/d0460e5c2d5d7f9bc5e6
//
// @param {string} $name Font name (human-readable)
// @param {string} $file Name of the font file
// @param {int} $weight font-weight value of the font file
// @param {string} $style font-style value of the font file
///
@mixin -cdd-font-face($name, $file, $weight: normal, $style: normal) {
@font-face {
font-family: quote($name);
font-style: $style;
font-weight: $weight;
src: url(-cdd-cachebuster('/assets/fonts/' + $file + '.eot')); // IE < 9
src: url(-cdd-cachebuster('/assets/fonts/' + $file + '.eot') + '?#') format('embedded-opentype'), // IE 9
url(-cdd-cachebuster('/assets/fonts/' + $file + '.woff2')) format('woff2'),
url(-cdd-cachebuster('/assets/fonts/' + $file + '.woff')) format('woff');
}
}

@ -0,0 +1,57 @@
body {
// Make sure that the footer is always at the bottom of the page
min-height: 100vh;
display: flex;
flex-direction: column;
// Hide horizontal overflow (required for animated contact forms)
overflow-x: hidden;
}
.main {
flex: 1;
// Fallback as IE does not respect min-height flex containers
flex-basis: auto;
}
// Page padding for print
@media print {
@page {
margin: 1.5cm;
}
}
// Visually hidden
.vh {
position: absolute;
top: -9999px;
left: -9999px;
}
// Container mixins
@mixin container {
width: 100%;
padding: 0 1.5rem;
margin: 0 auto;
@media (max-width: 30em) {
padding: 0 1rem;
}
@media print {
padding: 0;
}
}
@mixin container--full {
@include container;
max-width: 60rem;
}
@mixin container--text {
@include container;
max-width: 42rem;
}

@ -0,0 +1,4 @@
img, svg {
max-width: 100%;
height: auto;
}

@ -0,0 +1,38 @@
html {
// Use border-box box sizing
box-sizing: border-box;
// Disable text size adjustment on device rotation
text-size-adjust: 100%;
// Always display vertical scrollbar
overflow-y: scroll;
}
*, *::before, *::after {
// Reset all spacing
margin: 0;
padding: 0;
// Inherit box-sizing from html element
box-sizing: inherit;
// Disable default styling for certain elements
list-style: none;
border: none;
box-shadow: none;
}
// Disable all vertical margins for first and last childs of a container
:first-child {
margin-top: 0;
}
:last-child {
margin-bottom: 0;
}
// Make old browsers understand HTML5 elements
article, aside, details, figcaption, figure,
footer, header, main, menu, nav, section, summary {
display: block;
}

@ -0,0 +1,5 @@
# Font Squirrel Font-face Generator Configuration File
# Upload this file to the generator to recreate the settings
# you used to create these fonts.
{"mode":"expert","formats":["woff","woff2","eotz"],"tt_instructor":"keep","fix_gasp":"xy","fix_vertical_metrics":"N","metrics_ascent":"","metrics_descent":"","metrics_linegap":"","add_spaces":"Y","add_hyphens":"Y","fallback":"none","fallback_custom":"100","webonly":"Y","options_subset":"advanced","subset_range":["macroman","typographics","math","english","german"],"subset_custom":"","subset_custom_range":"","subset_ot_features_list":"","css_stylesheet":"stylesheet.css","filename_suffix":"","spacing_adjustment":"0","rememberme":"Y"}

@ -0,0 +1,65 @@
<?php $randomLink = str::random(5); ?>
<div class="preview-canvas text">
<h1>Typography preview</h1>
<h2>Lorem Ipsum</h2>
<p><strong>Lorem ipsum</strong> dolor sit amet, consectetur adipisicing elit. Voluptas repellendus modi ea <a href="https://example.com/<?= $randomLink ?>">praesentium</a> totam, earum, accusamus <em>pariatur</em> cumque. Ä, quia, dolore! Reiciendis, <a href="https://example.com">odio</a> iure accusamus consequatur eveniet voluptates quibusdam <strong>consequuntur</strong>!</p>
<h2>Dolor sit amet</h2>
<p>Lorem ipsum dolor sit amet.</p>
<ul>
<li>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</li>
<li>Üt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</li>
<li>Duis aute irure dolor in reprehenderit!</li>
</ul>
<p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<ol>
<li>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</li>
<li>Üt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</li>
<li>Duis aute irure dolor in reprehenderit!</li>
</ol>
<p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<h3>Consectetur adipisicing elit</h3>
<p><strong>Lorem ipsum</strong> dolor sit amet, consectetur adipisicing elit. Voluptas repellendus modi ea <a href="https://example.com/<?= $randomLink ?>">praesentium</a> totam, earum, accusamus <em>pariatur</em> cumque. Ä, quia, dolore! Reiciendis, <a href="https://example.com">odio</a> iure accusamus consequatur eveniet voluptates quibusdam <strong>consequuntur</strong>!</p>
<blockquote>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</blockquote>
<h2>Voluptas repellendus modi</h2>
<p><strong>Lorem ipsum</strong> dolor sit amet, consectetur adipisicing elit. Voluptas repellendus modi ea <a href="https://example.com/<?= $randomLink ?>">praesentium</a> totam, earum, accusamus <em>pariatur</em> cumque. Ä, quia, dolore! Reiciendis, <a href="https://example.com">odio</a> iure accusamus consequatur eveniet voluptates quibusdam <strong>consequuntur</strong>!</p>
<hr>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</p>
<h1>Heading 1</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</p>
<h2>Heading 2</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</p>
<h3>Heading 3</h3>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</p>
<h4>Heading 4</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</p>
<h5>Heading 5</h5>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</p>
<h6>Heading 6</h6>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</p>
</div>

@ -0,0 +1,119 @@
// @font-face definitions
@include -cdd-font-face('Input Sans Narrow', 'input-sans-narrow-regular', normal, normal);
@include -cdd-font-face('Input Sans Narrow', 'input-sans-narrow-italic', normal, italic);
@include -cdd-font-face('Input Sans Narrow', 'input-sans-narrow-bold', bold, normal);
@include -cdd-font-face('Input Serif Narrow', 'input-serif-narrow-bold', bold, normal);
:root {
font-size: 112.5%;
line-height: 1.7;
font-family: "Input Sans Narrow", Tahoma, Helvetica, sans-serif;
word-spacing: -0.1em;
// Make everything slightly smaller on small screens
@media (max-width: 30em) {
font-size: 100%;
}
}
h1, h2, h3, h4, h5, h6 {
font-family: "Input Serif Narrow", "Hoefler Text", Palatino, serif;
line-height: 1.35;
text-rendering: optimizeLegibility;
margin-top: 1.3em;
margin-bottom: 0.9em;
@media print {
page-break-after: avoid;
}
}
h1 {
font-size: -cdd-rem(36);
}
h2 {
font-size: -cdd-rem(27);
}
h3 {
font-size: -cdd-rem(20);
margin-bottom: 0.6em;
}
h4 {
font-size: -cdd-rem(18);
}
h5 {
font-size: -cdd-rem(18);
font-variant: small-caps;
}
h6 {
font-size: -cdd-rem(14);
text-transform: uppercase;
margin-top: 1.6em;
}
a {
transition: color 0.2s ease;
&:link, &:visited {
text-decoration: none;
color: $color--brand-primary;
}
&:hover, &:focus, &:active {
color: $color--brand-primary-alternate;
}
}
hr {
height: 0;
margin-top: 2em;
margin-bottom: 2em;
border-top: -cdd-rem(2) solid $color--passive;
@media print {
page-break-after: avoid;
}
}
small {
font-size: -cdd-rem(14);
}
// Content within Kirbytext
.text {
&-container {
@include container--text;
}
p:not(:last-child), ul:not(:last-child),
ol:not(:last-child), blockquote:not(:last-child) {
margin-bottom: 1em;
}
ul, ol {
padding-left: 40px;
}
ul li {
list-style: disc;
}
ol li {
list-style: decimal;
}
li:not(:last-child) {
margin-bottom: 0.1em;
}
a:visited {
color: $color--brand-primary-alternate;
}
blockquote {
padding: 0.5em 1em;
background: $color--passive-light;
border-left: 0.7em solid $color--passive;
border-radius: 0 0.3em 0.3em 0;
}
}

@ -0,0 +1,11 @@
<?php
return [
'defaults' => [
'text' => 'Learn more',
'label' => false,
'link' => url(),
'modifiers' => [],
'class' => false
]
];

@ -0,0 +1,12 @@
<?php
if(!is_array($modifiers)) $modifiers = [$modifiers];
$modifiers = array_map(function($modifier) {
return 'button--' . $modifier;
}, $modifiers);
$modifiers = implode(' ', $modifiers);
$classes = 'button' . r($modifiers, ' ' . $modifiers) . r($class, ' ' . $class);
?>
<a href="<?= $link ?>" class="<?= $classes ?>"<?php if($label): ?> aria-label="<?= $label ?>"<?php endif ?>><?= $text ?></a>

@ -0,0 +1,45 @@
.button {
display: inline-block;
text-align: center;
font: inherit;
line-height: inherit;
background: none;
cursor: pointer;
padding: 0.3em 0.5em 0.2em;
border: 1px solid $color--brand-primary;
border-radius: -cdd-em(4);
& + & {
margin-top: 0.5em;
}
transition: color 0.2s ease, border-color 0.2s ease;
&, &:link, &:visited {
color: $color--brand-primary;
}
&:hover, &:focus, &:not(&--novisited):visited {
border-color: $color--brand-primary-alternate;
color: $color--brand-primary-alternate;
}
&--secondary {
&, &:link, &:visited {
border-color: $color--brand-secondary;
color: $color--brand-secondary;
}
&:hover, &:focus, &:not(.button--novisited):visited {
border-color: $color--brand-secondary-alternate;
color: $color--brand-secondary-alternate;