Skip to content

Commit

Permalink
Merge pull request #223 from openameba/feature/dark-theme
Browse files Browse the repository at this point in the history
Implement Dark Theme
  • Loading branch information
herablog authored Jul 16, 2021
2 parents fce05e0 + 9e791d2 commit 351e67d
Show file tree
Hide file tree
Showing 27 changed files with 366 additions and 86 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publish-webbundle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
# - add hosting domain
find public -type f | sed -e 's|/index.html||g' -e '/^$/d' -e 's|public|https://a11y-guidelines.ameba.design|g' > urls.txt
# Add extra dependencies
echo -e 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/a11y-light.min.css \nhttps://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.min.js' >> urls.txt
echo -e 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.min.js' >> urls.txt
# Generate web bundle from URL list by following reasons:
# - static directory is different from a web server implementation e.g. directory index
# - to ensure HTTP response headers
Expand Down
15 changes: 12 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"version": "0.0.0",
"author": "CyberAgent, Inc.",
"dependencies": {
"ameba-color-palette.css": "^4.0.0",
"@openameba/spindle-icons": "^0.13.0",
"@openameba/spindle-syntax-themes": "^0.1.0",
"ameba-color-palette.css": "github:openameba/ameba-color-palette.css#feature/dark-mode",
"postcss": "^8.2.6"
},
"devDependencies": {
Expand Down Expand Up @@ -73,6 +75,7 @@
"build": "npm-run-all build:*",
"build:css": "postcss src/styles/*.css --dir public/css",
"build:html": "NODE_ENV=production eleventy",
"build:svg": "npx cpx 'node_modules/@openameba/spindle-icons/dist/svg/+(circle_bold|minus_bold|cross_bold|plus_bold).svg' src/img/icon",
"fixpack": "npx fixpack",
"format": "run-p format:*",
"format:prettier": "prettier --write {src,npm_scripts,.eleventy.js,package.json}",
Expand Down
4 changes: 3 additions & 1 deletion src/_includes/partials/footer.njk
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
<small class="Footer__Credit">
Copyright © 2017 CyberAgent, Inc. All Rights Reserved.
<a href="https://www.cyberagent.co.jp/">
<img src="/img/logo-cyberagent.svg" alt="CyberAgent" width="120" height="20">
<img src="/img/logo-cyberagent.svg" alt="CyberAgent" class="Theme--light" width="120" height="20">
<img src="/img/logo-cyberagent-inverse.svg" alt="CyberAgent" class="Theme--dark" width="120" height="20">
</a>
</small>
<spindle-theme-switch class="Footer__ThemeSwitch"></spindle-theme-switch>
</div>
</footer>
6 changes: 4 additions & 2 deletions src/_includes/partials/header.njk
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
<div class="Container">
<h1>
<a href="/">
<img src="/img/logo-ameba.svg" class="Header__Logo" alt="Ameba" width="144" height="44" />
<img src="/img/logo-ameba.svg" class="Header__Logo Theme--light" alt="Ameba" width="144" height="44" />
<img src="/img/logo-ameba-inverse.svg" class="Header__Logo Theme--dark" alt="Ameba" width="144" height="44" />
<span class="Header__title">Accessibility<br>Guidelines</span>
</a>
</h1>
</div>
{% else %}
<p class="Container">
<a href="/">
<img src="/img/logo-ameba.svg" class="Header__Logo" alt="Ameba" width="144" height="44" />
<img src="/img/logo-ameba.svg" class="Header__Logo Theme--light" alt="Ameba" width="144" height="44" />
<img src="/img/logo-ameba-inverse.svg" class="Header__Logo Theme--dark" alt="Ameba" width="144" height="44" />
<span class="Header__title">Accessibility<br>Guidelines</span>
</a>
</p>
Expand Down
3 changes: 2 additions & 1 deletion src/_includes/partials/meta.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
<title>{{ .Site.Title }}</title>
{{ end }}
<link rel="icon" href="{{ .Site.BaseURL }}/favicon.ico">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.3/styles/a11y-light.min.css">
<link rel="stylesheet" href="/css/highlight.js-light.css" media="(prefers-color-scheme: light)">
<link rel="stylesheet" href="/css/highlight.js-dark.css" media="(prefers-color-scheme: dark)">
{{ if eq .Params.pageType "default" }}
<link rel="stylesheet" href="/css/default.css">
{{ else }}
Expand Down
3 changes: 2 additions & 1 deletion src/_includes/partials/meta.njk
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
<title>{{ pageTitle }}</title>

<link rel="icon" href="{{ site.baseURL }}/favicon.ico">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/a11y-light.min.css">
<link rel="stylesheet" href="/css/highlight.js-light.css" media="(prefers-color-scheme: light)">
<link rel="stylesheet" href="/css/highlight.js-dark.css" media="(prefers-color-scheme: dark)">

{% if pageType === "default" %}
<link rel="stylesheet" href="/css/default.css">
Expand Down
263 changes: 261 additions & 2 deletions src/_includes/partials/script.njk
Original file line number Diff line number Diff line change
@@ -1,5 +1,264 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.6.0/highlight.min.js"></script>
<script>
<script type="module">
const THEME = 'theme';
const THEME_CHANGE_EVENT = 'themechange';
const LIGHT = 'light';
const DARK = 'dark';
const MATCH_MEDIA_CONDITION = '(prefers-color-scheme: dark)';
const lightCss = document.querySelectorAll('link[media*="prefers-color-scheme"][media*="light"]');
const darkCss = document.querySelectorAll('link[media*="prefers-color-scheme"][media*="dark"]');
const store = localStorage;
const STORE_KEY = 'spindle-theme-switch';
export class SpindleThemeSwitch extends HTMLElement {
static get observedAttributes() {
return [THEME];
}
constructor() {
super();
// Props attached via attributes
this.label = this.getAttribute('label');
this._handleMediaQueryChange = this._handleMediaQueryChange.bind(this);
this._handleInputChange = this._handleInputChange.bind(this);
// Render switch
this.attachShadow({ mode: 'open' });
this._render();
}
connectedCallback() {
this.form = this.shadowRoot.querySelector('#spindle-theme-switch');
this.form.addEventListener('change', this._handleInputChange, false);
// Initialize theme
const mql = matchMedia(MATCH_MEDIA_CONDITION);
const preference = mql.matches ? DARK : LIGHT;
const storedTheme = store.getItem(STORE_KEY);
const theme = storedTheme || preference;
this._updateTheme(theme);
// Listen device's preference changes
mql.addEventListener('change', this._handleMediaQueryChange, false);
}
disconnectedCallback() {
mql.removeEventListener('change', this._handleMediaQueryChange, false);
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === THEME) {
if (![LIGHT, DARK].includes(newValue)) {
throw new Error(`Allowed values are ${LIGHT} and ${DARK}`);
}
this._updateTheme(newValue);
}
}
_dispatchEvent(type, value) {
this.dispatchEvent(new CustomEvent(type, {
bubbles: true,
composed: true,
detail: value,
}));
}
_updateDOM() {
// ameba-color-palette.css detects this
document.documentElement.dataset.theme = this.theme;
// Apply appropriate css with media attribute
if (this.theme === DARK) {
[...lightCss].forEach((link) => {
link.media = 'not all';
link.disabled = true;
});
[...darkCss].forEach((link) => {
link.media = 'all';
link.disabled = false;
});
} else {
[...lightCss].forEach((link) => {
link.media = 'all';
link.disabled = false;
});
[...darkCss].forEach((link) => {
link.media = 'not all';
link.disabled = true;
});
}
}
_updateTheme(newTheme) {
if (newTheme === this.theme) {
return;
}
this.theme = newTheme;
this._updateDOM();
const radioToCheck = this.form.querySelector(`input[value="${this.theme}"]`);
radioToCheck.checked = true;
store.setItem(STORE_KEY, this.theme);
this._dispatchEvent(THEME_CHANGE_EVENT, { theme: this.theme });
}
_handleMediaQueryChange(event) {
const preference = event.matches ? DARK : LIGHT;
this._updateTheme(preference);
}
_handleInputChange(event) {
const theme = event.target.value === DARK ? DARK : LIGHT;
this._updateTheme(theme);
}
_render() {
const label = this.label || 'テーマを切り替える';
this.shadowRoot.innerHTML = `
<style>
:host {
display: inline-block;
-webkit-tap-highlight-color: var(--gray-5-alpha);
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
clip: rect(1px, 1px, 1px, 1px);
}
fieldset {
border: none;
margin: 0;
padding: 0;
}
.theme-switch {
align-items: center;
display: flex;
}
.switch {
align-items: center;
background: var(--color-surface-tertiary);
border-radius: 40px;
box-sizing: border-box;
display: flex;
height: 34px;
list-style: none;
margin: 0;
padding: 4px;
position: relative;
top: 0;
width: 84px;
}
.switch-theme-background {
background: var(--color-surface-primary);
border-radius: 40px;
content: '';
display: block;
height: 26px;
left: 4px;
position: absolute;
top: 4px;
transform: translate(100%, 0);
transition: transform ease-out 0.3s, background 0.3s;
width: 38px;
}
.theme-dark:checked ~ .switch-theme-background {
background: var(--color-surface-quaternary);
transform: translate(0, 0);
}
input[type="radio"] {
-webkit-appearance: none;
appearance: none;
border: none;
margin: 0;
}
.theme {
align-items: center;
background-position: center;
background-repeat: no-repeat;
background-size: 18px;
display: flex;
flex: 0 0 50%;
height: 100%;
justify-content: center;
z-index: 1;
width: 100%;
}
.theme-dark {
background-image: url('data:image/svg+xml;utf8, <svg fill="rgba(8, 18, 26, 0.3)" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M10.86 21a9.03 9.03 0 0 1-5.58-1.92c-.67-.52-.94-1.37-.7-2.17.25-.81.96-1.37 1.82-1.42.75-.05 1.51-.25 2.24-.58 1.98-.9 3.42-2.73 3.76-4.79.22-1.34.04-2.69-.53-3.89-.37-.78-.23-1.67.36-2.29.58-.6 1.45-.77 2.23-.45 3.78 1.62 5.97 5.46 5.46 9.55-.49 3.94-3.56 7.16-7.46 7.82-.53.1-1.07.14-1.6.14zm2.82-15.67.01.04c.75 1.57.99 3.33.7 5.07-.45 2.7-2.33 5.11-4.9 6.28-.96.43-1.94.69-2.94.76 1.56 1.26 3.56 1.76 5.59 1.41 3.04-.51 5.43-3.02 5.81-6.09.39-3.21-1.32-6.21-4.27-7.47z"/></svg>');
}
.theme-dark:checked {
background-image: url('data:image/svg+xml;utf8, <svg fill="%23f5e100" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M13.07 3c5.17 1.25 8.38 6.98 6.28 12.22-1.33 3.34-4.59 5.66-8.18 5.77-2.75.09-5.23-1.05-6.96-2.9-.62-.67-.04-1.76.86-1.65.78.1 1.59.07 2.43-.09 2.99-.59 5.38-3.03 5.89-6.03.36-2.14-.18-4.16-1.31-5.72-.55-.76.08-1.82.99-1.6z"/></svg>');
}
.theme-light {
background-image: url('data:image/svg+xml;utf8, <svg fill="rgba(255, 255, 255, 0.16)" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 16.5c-2.48 0-4.5-2.02-4.5-4.5S9.52 7.5 12 7.5s4.5 2.02 4.5 4.5-2.02 4.5-4.5 4.5zM12 9c-1.65 0-3 1.35-3 3s1.35 3 3 3 3-1.35 3-3-1.35-3-3-3zm1-4.5V3c0-.55-.45-1-1-1s-1 .45-1 1v1.5c0 .55.45 1 1 1s1-.45 1-1zM13 21v-1.5c0-.55-.45-1-1-1s-1 .45-1 1V21c0 .55.45 1 1 1s1-.45 1-1zm5.01-13.6 1.06-1.06a.996.996 0 1 0-1.41-1.41L16.6 5.99a.996.996 0 0 0 .71 1.7c.26 0 .51-.09.7-.29zM6.34 19.07l1.06-1.06a.996.996 0 1 0-1.41-1.41l-1.06 1.06a.996.996 0 0 0 .71 1.7c.26 0 .51-.09.7-.29zM22 12c0-.55-.45-1-1-1h-1.5c-.55 0-1 .45-1 1s.45 1 1 1H21c.55 0 1-.45 1-1zM5.5 12c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1s.45 1 1 1h1.5c.55 0 1-.45 1-1zm13.57 7.07a.996.996 0 0 0 0-1.41l-1.06-1.06a.996.996 0 1 0-1.41 1.41l1.06 1.06c.2.2.45.29.71.29.26 0 .51-.09.7-.29zM7.4 7.4a.996.996 0 0 0 0-1.41L6.34 4.93a.996.996 0 1 0-1.41 1.41L5.99 7.4c.19.2.45.3.71.3.26 0 .51-.1.7-.3z"/></svg>');
}
.theme-light:checked {
background-image: url('data:image/svg+xml;utf8, <svg fill="%23de4d14" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M16.5 12c0 2.48-2.02 4.5-4.5 4.5S7.5 14.48 7.5 12 9.52 7.5 12 7.5s4.5 2.02 4.5 4.5zM12 5.5c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1s-1 .45-1 1v1.5c0 .55.45 1 1 1zm0 13c-.55 0-1 .45-1 1V21c0 .55.45 1 1 1s1-.45 1-1v-1.5c0-.55-.45-1-1-1zm5.3-10.8c.26 0 .51-.1.71-.29l1.06-1.06a.996.996 0 1 0-1.41-1.41L16.6 5.99a.996.996 0 0 0 0 1.41c.19.2.45.3.7.3zM5.99 16.6l-1.06 1.06a.996.996 0 0 0 .71 1.7c.26 0 .51-.1.71-.29l1.06-1.06a.996.996 0 0 0 0-1.41c-.39-.39-1.03-.39-1.42 0zM21 11h-1.5c-.55 0-1 .45-1 1s.45 1 1 1H21c.55 0 1-.45 1-1s-.45-1-1-1zM5.5 12c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1s.45 1 1 1h1.5c.55 0 1-.45 1-1zm12.51 4.6a.996.996 0 1 0-1.41 1.41l1.06 1.06c.2.2.45.29.71.29.26 0 .51-.1.71-.29a.996.996 0 0 0 0-1.41l-1.07-1.06zM5.99 7.4c.19.2.45.3.71.3.26 0 .51-.1.7-.3a.996.996 0 0 0 0-1.41L6.34 4.93a.996.996 0 1 0-1.41 1.41L5.99 7.4z"/></svg>');
}
.label {
color: var(--color-text-medium-emphasis);
font-size: calc(12 / 16 * 1rem);
font-weight: bold;
margin: 0 0 0 1rem;
}
@media (prefers-reduced-motion: reduce) {
.themes::before {
transition: transform 0.1s, background 0.1s;
}
}
</style>
<form class="theme-switch" id="spindle-theme-switch">
<fieldset>
<legend class="visually-hidden">${label}</legend>
<div class="switch">
<label class="visually-hidden" for="spindle-theme-switch-dark">ダークテーマ</label>
<input class="theme theme-dark" id="spindle-theme-switch-dark" name="theme" type="radio" value="${DARK}">
<label class="visually-hidden" for="spindle-theme-switch-light">ライトテーマ</label>
<input class="theme theme-light" id="spindle-theme-switch-light" name="theme" type="radio" value="${LIGHT}">
<span class="switch-theme-background"></span>
</div>
</fieldset>
<p aria-hidden="true" class="label" part="label">${label}</p>
</form>
`;
}
}
customElements.define('spindle-theme-switch', SpindleThemeSwitch);
</script>
<script type="module">
const mql = window.matchMedia('(prefers-reduced-motion: reduce)');
function applyThemeTransition () {
const transition = !mql || mql.matches ? 'background 0.1s' : 'background 0.3s ease-out';
document.body.style.transition = transition;
}
// To avoid applying the transition when initial page load
window.addEventListener('load', applyThemeTransition);
mql.addEventListener('change', applyThemeTransition, false);
</script>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.6.0/highlight.min.js"></script>
<script defer>
document.addEventListener('DOMContentLoaded', () => {
const blocks = document.querySelectorAll('pre code');
for (const block of blocks) {
Expand Down
7 changes: 3 additions & 4 deletions src/_shortcodes/label.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ const site = require('../_data/site')();
* @returns {string}
*/
const label = (content, label) => {
return `<span class="RuleLabel RuleLabel--${label}">
<img src="/img/icon/${label}.svg" width="24" height="24" alt="" />
${label.charAt(0).toUpperCase() + label.slice(1)}
</span>${content}`;
return `<span class="RuleLabel RuleLabel--${label}">${
label.charAt(0).toUpperCase() + label.slice(1)
}</span>${content}`;
};

module.exports = label;
Loading

0 comments on commit 351e67d

Please sign in to comment.