Skip to content

Instantly share code, notes, and snippets.

@mattduffield
Last active October 12, 2020 00:26
Show Gist options
  • Save mattduffield/b0283962343a37edc740f0869466f405 to your computer and use it in GitHub Desktop.
Save mattduffield/b0283962343a37edc740f0869466f405 to your computer and use it in GitHub Desktop.
Aurelia theme engine
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Dumber Gist</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<base href="/">
</head>
<!--
Dumber Gist uses dumber bundler, the default bundle file
is /dist/entry-bundle.js.
The starting module is pointed to "main" (data-main attribute on script)
which is your src/main.js.
-->
<body>
<my-app></my-app>
<script src="/dist/entry-bundle.js" data-main="main"></script>
</body>
</html>
{
"dependencies": {
"aurelia": "dev"
}
}
export default [
{
name: 'gray',
variables: {
'--color-100': '#F7FAFC',
'--color-200': '#EDF2F7',
'--color-300': '#E2E8F0',
'--color-400': '#CBD5E0',
'--color-500': '#A0AEC0',
'--color-600': '#718096',
'--color-700': '#4A5568',
'--color-800': '#2D3748',
'--color-900': '#1A202C'
},
},
{
name: 'red',
variables: {
'--color-100': '#FFF5F5',
'--color-200': '#FED7D7',
'--color-300': '#FEB2B2',
'--color-400': '#FC8181',
'--color-500': '#F56565',
'--color-600': '#E53E3E',
'--color-700': '#C53030',
'--color-800': '#9B2C2C',
'--color-900': '#742A2A'
},
},
{
name: 'orange',
variables: {
'--color-100': '#FFFAF0',
'--color-200': '#FEEBC8',
'--color-300': '#FBD38D',
'--color-400': '#F6AD55',
'--color-500': '#ED8936',
'--color-600': '#DD6B20',
'--color-700': '#C05621',
'--color-800': '#9C4221',
'--color-900': '#7B341E'
},
},
{
name: 'yellow',
variables: {
'--color-100': '#FFFFF0',
'--color-200': '#FEFCBF',
'--color-300': '#FAF089',
'--color-400': '#F6E05E',
'--color-500': '#ECC94B',
'--color-600': '#D69E2E',
'--color-700': '#B7791F',
'--color-800': '#975A16',
'--color-900': '#744210'
},
},
{
name: 'green',
variables: {
'--color-100': '#F0FFF4',
'--color-200': '#C6F6D5',
'--color-300': '#9AE6B4',
'--color-400': '#68D391',
'--color-500': '#48BB78',
'--color-600': '#38A169',
'--color-700': '#2F855A',
'--color-800': '#276749',
'--color-900': '#22543D'
},
},
{
name: 'blue',
variables: {
'--color-100': '#EBF8FF',
'--color-200': '#BEE3F8',
'--color-300': '#90CDF4',
'--color-400': '#63B3ED',
'--color-500': '#4299E1',
'--color-600': '#3182CE',
'--color-700': '#2B6CB0',
'--color-800': '#2C5282',
'--color-900': '#2A4365'
},
},
{
name: 'indigo',
variables: {
'--color-100': '#EBF4FF',
'--color-200': '#C3DAFE',
'--color-300': '#A3BFFA',
'--color-400': '#7F9CF5',
'--color-500': '#667EEA',
'--color-600': '#5A67D8',
'--color-700': '#4C51BF',
'--color-800': '#434190',
'--color-900': '#3C366B'
},
},
{
name: 'purple',
variables: {
'--color-100': '#FAF5FF',
'--color-200': '#E9D8FD',
'--color-300': '#D6BCFA',
'--color-400': '#B794F4',
'--color-500': '#9F7AEA',
'--color-600': '#805AD5',
'--color-700': '#6B46C1',
'--color-800': '#553C9A',
'--color-900': '#44337A'
},
},
{
name: 'pink',
variables: {
'--color-100': '#FFF5F7',
'--color-200': '#FED7E2',
'--color-300': '#FBB6CE',
'--color-400': '#F687B3',
'--color-500': '#ED64A6',
'--color-600': '#D53F8C',
'--color-700': '#B83280',
'--color-800': '#97266D',
'--color-900': '#702459'
},
},
];
export class ColorTheme {
constructor(name, css) {
this.name = name;
this.css = css;
this.keys = Object.keys(css);
}
applyTo(style) {
for (const key of this.keys) {
style.setProperty(key, this.css[key]);
}
}
initStyle(style) {
for (const key of this.keys) {
const partial = '--color';
const newName = `--${this.name}`;
const newKey = `${key.replace(partial, newName)}`;
style.setProperty(newKey, this.css[key]);
}
}
static fromJSON(json) {
return new ColorTheme(json.name, json.variables);
}
}
:root {
/* Assume Light Theme */
--bg-color: #F7FAFC;
--bg-tray-color: whitesmoke;
--color: var(--color-900);
--text-light-color: var(--gray-400);
--text-normal-color: var(--gray-600);
--text-dark-color: var(--gray-700);
--shadow: 0 2px 10px rgba(0, 0, 0, 1);
/* Assume Gray Color Theme */
--color-100: #F7FAFC;
--color-200: #EDF2F7;
--color-300: #E2E8F0;
--color-400: #CBD5E0;
--color-500: #A0AEC0;
--color-600: #718096;
--color-700: #4A5568;
--color-800: #2D3748;
--color-900: #1A202C;
/* Color Theme reference */
--color-lightest: var(--color-100);
--color-lighter: var(--color-200);
--color-light: var(--color-300);
--color-normal-light: var(--color-400);
--color-normal: var(--color-500);
--color-normal-dark: var(--color-600);
--color-dark: var(--color-700);
--color-darker: var(--color-800);
--color-darkest: var(--color-900);
}
body {
background-color: var(--bg-color);
color: var(--color)
}
h1 {
color: var(--color-darker);
}
label {
color: var(--color-dark);
}
import Aurelia, { StyleConfiguration } from 'aurelia';
import { MyApp } from './my-app';
import main from './main.css';
Aurelia
.register(
StyleConfiguration.shadowDOM({ sharedStyles: [main] }),
)
.app(MyApp)
.start();
/*
h1 {
color: var(--color-400);
}
*/
<use-shadow-dom></use-shadow-dom>
<import from="./my-element"></import>
<h1>${message}</h1>
<my-element></my-element>
<label>Theme</label>
<select value.bind="theme"
change.trigger="theme.applyTo(style)"
style="margin-top: 10px;">
<option repeat.for="item of themes"
model.bind="item">
${item.name}
</option>
</select>
<label>Color Theme</label>
<select value.bind="colorTheme"
change.trigger="colorTheme.applyTo(style)"
style="margin-top: 10px;">
<option repeat.for="item of colorThemes"
model.bind="item">
${item.name}
</option>
</select>
import {Theme} from './theme';
import {ColorTheme} from './color-theme';
export class MyApp {
message = 'Hello Aurelia 2!';
themes = [];
colorThemes = [];
style = document.documentElement.style;
async beforeBind() {
this.themes = (await import('./theme-data')).default.map(Theme.fromJSON);
this.colorThemes = (await import('./color-data')).default.map(ColorTheme.fromJSON);
for (const ct of this.colorThemes) {
ct.initStyle(this.style);
}
}
}
:host {
display: block;
/*
background-color: var(--color-darker);
color: var(--color-lighter);
*/
}
/*
:host label {
color: var(--text-normal-color);
}
*/
<use-shadow-dom></use-shadow-dom>
<label>Hello World</label>
export class MyElement {
}
export default [
{
name: 'theme-light',
variables: {
'--bg-color': '#F7FAFC',
'--color': 'var(--color-900)',
'--text-light-color': 'var(--gray-400)',
'--text-normal-color': 'var(--gray-600)',
'--text-dark-color': 'var(--gray-700)',
'--text-color': 'var(--gray-600)',
'--shadow': '0 2px 10px rgba(0, 0, 0, 1)',
'--label-color': '#4A5568'
},
},
{
name: 'theme-dark',
variables: {
'--bg-color': '#1A202C',
'--color': 'var(--color-200)',
'--text-light-color': 'var(--gray-500)',
'--text-normal-color': 'var(--gray-300)',
'--text-dark-color': 'var(--gray-100)',
'--text-color': 'var(--gray-800)',
'--shadow': '0 2px 10px #718096'
},
},
];
export class Theme {
constructor(name, css) {
this.name = name;
this.css = css;
this.keys = Object.keys(css);
}
applyTo(style) {
for (const key of this.keys) {
style.setProperty(key, this.css[key]);
}
}
static fromJSON(json) {
return new Theme(json.name, json.variables);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment