Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use spindle-theme-switch #7

Merged
merged 1 commit into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions blog.keiya01.dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"devDependencies": {
"@11ty/eleventy": "^0.12.1",
"@11ty/eleventy-plugin-syntaxhighlight": "^3.1.3",
"@openameba/spindle-theme-switch": "^0.2.2",
"@squoosh/lib": "^0.4.0",
"@textlint/textlint-plugin-markdown": "^12.0.2",
"@types/common-tags": "^1.8.1",
Expand Down
5 changes: 2 additions & 3 deletions blog.keiya01.dev/src/layouts/components/global/Header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,8 @@ export const Header = ({
</li>
<li class="${style.item} ${style.schemeButton}">
<color-scheme-button
aria-disabled="true"
icon-size="24"
label="テーマを変更する"
appearance="switch"
legend="テーマを切り替える"
></color-scheme-button>
</li>
</ul>
Expand Down
50 changes: 10 additions & 40 deletions blog.keiya01.dev/src/lib/components/global/color-scheme-button.css
Original file line number Diff line number Diff line change
@@ -1,42 +1,12 @@
:root {
--color-scheme-button-icon-size: 24px;
--color-scheme-button-icon-color--dark: #edcd00;
--color-scheme-button-icon-color--light: #ed4f00;
--color-scheme-button-color--on: #a770d4;
--color-scheme-button-color--off: #fff;
--color-scheme-button-outline-color: #870144;
}

color-scheme-button[aria-disabled="true"] {
display: inline-block;
opacity: 0.3;
border-radius: var(--color-scheme-button-icon-size);
padding: 3px;
background: var(--color-scheme-button-color--on);
position: relative;
width: calc(var(--color-scheme-button-icon-size) * 2 + 10px);
height: var(--color-scheme-button-icon-size);
}

color-scheme-button[aria-disabled="true"]::before {
content: "";
display: inline-flex;
border-radius: 50%;
width: calc(var(--color-scheme-button-icon-size) + 5px);
height: var(--color-scheme-button-icon-size);
background: var(--color-scheme-button-color--off);
position: absolute;
top: 3px;
transform: translateX(100%);
transition: transform ease-in 0.1s, background ease-in 0.1s;
}
color-scheme-button {
--light-mode-switch-background: #9b92a2;
--dark-mode-switch-background: #53485b;
--light-mode-focus-outline-color: #870144;
--dark-mode-focus-outline-color: #870144;
--light-mode-checked-background: #dcd1e6;
--dark-mode-checked-background: rgba(255, 255, 255, 0.26);

@media (prefers-color-scheme: light) {
color-scheme-button[aria-disabled="true"] {
background: var(--color-scheme-button-color--off);
}
color-scheme-button[aria-disabled="true"]::before {
transform: translateX(0);
background: var(--color-scheme-button-color--on);
}
display: block;
height: 34px;
width: 84px;
}
235 changes: 2 additions & 233 deletions blog.keiya01.dev/src/lib/components/global/color-scheme-button.ts
Original file line number Diff line number Diff line change
@@ -1,236 +1,5 @@
import {
getDataFromLocalStorage,
setDataToLocalStorage,
} from "../../fundamentals/localstrage";
import { SpindleThemeSwitch } from "@openameba/spindle-theme-switch";

const DataColorScheme = "data-color-scheme";

type ColorSchemeUnion = "dark" | "light";

const ColorScheme: { [K in ColorSchemeUnion]: ColorSchemeUnion } = {
dark: "dark",
light: "light",
};

const getCurrentColorScheme = (name: string): ColorSchemeUnion =>
ColorScheme[name as ColorSchemeUnion] || ColorScheme.dark;

const getNextColorScheme = (isDark: boolean): ColorSchemeUnion =>
isDark ? ColorScheme.light : ColorScheme.dark;

const toggleColorScheme = (event: Event) => {
const target = event.target as ColorSchemeButton | null;
if (!target) {
return;
}

const isChecked = target.checked === true;
target.checked = !isChecked;

const html = document.documentElement;
const currentColor = getCurrentColorScheme(
html.getAttribute(DataColorScheme) || ""
);
const nextColor = getNextColorScheme(currentColor === ColorScheme.dark);
html.setAttribute(DataColorScheme, nextColor);

setDataToLocalStorage("theme", nextColor);
};

const setInitialColorScheme = async (matchDark: boolean) => {
const currentColor = await getDataFromLocalStorage("theme");

const isDark = currentColor ? currentColor === ColorScheme.dark : matchDark;

if (currentColor) {
document.documentElement.setAttribute(DataColorScheme, currentColor);
} else {
document.documentElement.setAttribute(
DataColorScheme,
isDark ? ColorScheme.dark : ColorScheme.light
);
}

return isDark;
};

class ColorSchemeButton extends HTMLElement {
constructor() {
super();

this.addEventListener("click", toggleColorScheme);

this.attachShadow({ mode: "open" });

this.render();
}

async connectedCallback(): Promise<void> {
const mql = window.matchMedia(
`(prefers-color-scheme: ${ColorScheme.dark})`
);
this.checked = await setInitialColorScheme(mql.matches);
mql.addEventListener("change", async (e) => {
this.checked = await setInitialColorScheme(e.matches);
});

if (!this.disabled) {
throw new Error(
"<color-scheme-button> should has `aria-disabled` attribute at initial rendering"
);
}
this.disabled = false;
}

set disabled(disabled: boolean) {
this.setAttribute("aria-disabled", `${disabled}`);
}

get disabled(): boolean {
return this.getAttribute("aria-disabled") === "true";
}

set checked(checked: boolean) {
this.setAttribute("aria-checked", `${checked}`);
}

get checked(): boolean {
return this.getAttribute("aria-checked") === "true";
}

set colorScheme(colorScheme: ColorSchemeUnion) {
this.setAttribute(DataColorScheme, colorScheme);
}

get colorScheme(): ColorSchemeUnion {
return getCurrentColorScheme(this.getAttribute(DataColorScheme) || "");
}

get label(): string {
return this.getAttribute("label") || "";
}

get iconSize(): string {
return this.getAttribute("icon-size") || "100%";
}

darkIcon = (label: string): string =>
`<svg aira-label="${label}" fill="currentColor" height="${this.iconSize}" viewBox="0 0 20 20" width="${this.iconSize}" xmlns="http://www.w3.org/2000/svg"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg>`;

lightIcon = (label: string): string =>
`<svg aira-label="${label}" fill="currentColor" height="${this.iconSize}" viewBox="0 0 20 20" width="${this.iconSize}" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" clip-rule="evenodd"></path></svg>`;

getStyle(): string {
return `
:host {
display: inline-flex;
}

button {
display: flex;
justify-content: space-between;
border-radius: var(--color-scheme-button-icon-size);
padding: 3px;
position: relative;
box-sizing: content-box;
background: var(--color-scheme-button-color--on);
width: calc(var(--color-scheme-button-icon-size) * 2 + 10px);
height: var(--color-scheme-button-icon-size);
transition: background ease-in 0.1s;
outline: none;
border: none;
}

button:focus {
box-shadow: 0 0 0 3px var(--color-scheme-button-outline-color);
}

button:focus:not(:focus-visible) {
box-shadow: none;
}

button::before {
content: "";
display: inline-block;
border-radius: var(--color-scheme-button-icon-size);
width: calc(var(--color-scheme-button-icon-size) + 5px);
height: var(--color-scheme-button-icon-size);
background: var(--color-scheme-button-color--off);
position: absolute;
top: 3px;
transform: translateX(100%);
transition: transform ease-in 0.1s, background ease-in 0.1s;
}

@media(prefers-reduced-motion: reduce) {
button {
transition: none;
}

button::before {
transition: none;
}
}

:host(color-scheme-button[aria-checked="false"]) > label > button {
background: var(--color-scheme-button-color--off);
}

:host(color-scheme-button[aria-checked="false"]) > label > button::before {
transform: translateX(0);
background: var(--color-scheme-button-color--on);
}

label {
display: inline-block;
}

img {
width: calc(var(--color-scheme-button-icon-size));
height: var(--color-scheme-button-icon-size);
padding: 0 3px;
}

.dark-icon {
color: var(--color-scheme-button-icon-color--dark);
}

.light-icon {
color: var(--color-scheme-button-icon-color--light);
}
`;
}

render(): void {
const shadow = this.shadowRoot;
if (!shadow) {
return;
}

shadow.innerHTML = `
<style>${this.getStyle()}</style>
<label for="switch">
<button aria-label="${this.label}" aria-checked="${
this.checked
}" type="button" id="switch" role="switch">
<span class="dark-icon">${this.darkIcon("ダークテーマ")}</span>
<span class="light-icon">${this.lightIcon("ライトテーマ")}</span>
</button>
</label>
`;
}
}
class ColorSchemeButton extends SpindleThemeSwitch {}

customElements.define("color-scheme-button", ColorSchemeButton);

declare global {
interface Window {
ColorSchemeButton: typeof ColorSchemeButton;
}

interface HTMLElementTagNameMap {
"color-scheme-button": ColorSchemeButton;
}
}

export default ColorSchemeButton;
12 changes: 12 additions & 0 deletions blog.keiya01.dev/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"

"@openameba/spindle-theme-switch@^0.2.2":
version "0.2.2"
resolved "https://registry.yarnpkg.com/@openameba/spindle-theme-switch/-/spindle-theme-switch-0.2.2.tgz#e99e2d0f8e8af3d46b3574c4825c31dc5c88c9c3"
integrity sha512-GB5EgQBnOveSY5K8skmLH+FW0bGmaNQdFfTvajecnQBkKYaDfvHVSCIrMMBCQGnwHpWp7+U3jDaS1onJz2Pq5g==
dependencies:
dark-mode-toggle "^0.14.0"

"@squoosh/lib@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@squoosh/lib/-/lib-0.4.0.tgz#31d18cb082c69e404589e2e281414d10f91e1668"
Expand Down Expand Up @@ -1473,6 +1480,11 @@ csstype@^3.0.7:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340"
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==

dark-mode-toggle@^0.14.0:
version "0.14.2"
resolved "https://registry.yarnpkg.com/dark-mode-toggle/-/dark-mode-toggle-0.14.2.tgz#77ab6d8e8b7e1505d8d06192f0d1d5cb187e7787"
integrity sha512-VsFLzTO9lA7Pfbylrd6JjRlJkFHTOz6LwoXSeJQKmc3xkLffkyUOzB/R8tDDxXTj3duyaBeLLzqARjiOmV3LNA==

date-time@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/date-time/-/date-time-0.1.1.tgz#ed2f6d93d9790ce2fd66d5b5ff3edd5bbcbf3b07"
Expand Down