Changes
27 changed files (+494/-12)
-
-
-
@@ -29,6 +29,9 @@ },"markup": { "useTabs": true }, "malva": { "useTabs": true }, "plugins": [ "https://plugins.dprint.dev/markdown-0.17.8.wasm", // "Biome is a fast formatter for JavaScript, TypeScript, JSX, TSX, JSON, CSS and GraphQL..."
-
@@ -37,6 +40,9 @@ "https://plugins.dprint.dev/exec-0.5.0.json@8d9972eee71fa1590e04873540421f3eda7674d0f1aae3d7c788615e7b7413d0","https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.0.wasm", // "HTML, Vue, Svelte, Astro, Angular, Jinja, Twig, Nunjucks, and Vento files via markup_fmt." "https://plugins.dprint.dev/g-plane/markup_fmt-v0.18.0.wasm", "https://plugins.dprint.dev/toml-0.6.3.wasm" "https://plugins.dprint.dev/toml-0.6.3.wasm", // "Configurable, smart and fast CSS, SCSS, Sass and Less formatter..." // Biome の CSS は `:state()` がパースエラーになり使い物にならないため、品質の高いこちらを利用している。 "https://plugins.dprint.dev/g-plane/malva-v0.11.0.wasm" ] }
-
-
-
@@ -4,6 +4,7 @@ "private": true,"workspaces": ["packages/*"], "scripts": { "lint": "bun run --filter '*' lint", "dev": "bun run --filter '*' dev", "build": "bun run --filter '*' build", "clean": "bun run --filter '*' clean" },
-
-
packages/gui/.gitignore (new)
-
@@ -0,0 +1,16 @@# このディレクトリ特有の無視設定。 # # SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> # SPDX-License-Identifier: AGPL-3.0-only # What: ビルドされたドキュメントページとアセット。 # Why: 編集するものではないため。 /docs-dist # What: ビルドされたライブラリ JS 。 # Why: 編集するものではないため。 /dist # What: ビルドされた *.d.ts ファイルを格納するディレクトリ。 # Why: 自動生成されたディレクトリのため。 /types
-
-
packages/gui/REUSE.toml (new)
-
@@ -0,0 +1,13 @@# このディレクトリ配下のファイルを reuse-tool が扱う際の設定。 # # SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> # SPDX-License-Identifier: AGPL-3.0-only version = 1 # このパッケージの HTML はドキュメント用途であり、且つ複数ページあるため # いちいち `*.html.license` ファイルを作成するのは非常に邪魔なのでまとめて指定。 [[annotations]] path = "src/**/*.html" SPDX-FileCopyrightText = "2024 Shota FUJI <pockawoooh@gmail.com>" SPDX-License-Identifier = "AGPL-3.0-only"
-
-
-
@@ -0,0 +1,27 @@{ "name": "@yamori/gui", "private": true, "type": "module", "scripts": { "dev": "vite", "build:lib": "TARGET=lib vite build && tsc -p tsconfig.build.jsonc", "build:docs": "vite build", "build:all": "bun build:lib && bun build:docs", "build": "bun build:lib", "clean": "rm -rf dist docs-dist types", "prepare": "bun build:lib" }, "exports": { ".": { "types": "./types/lib.d.ts", "default": "./dist/lib.js" }, "./*.css": { "default": "./dist/*.css" } }, "devDependencies": { "typescript": "^5.7.2", "vite": "^6.0.2" } }
-
-
-
@@ -0,0 +1,2 @@SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> SPDX-License-Identifier: AGPL-3.0-only
-
-
-
@@ -0,0 +1,8 @@// ドキュメントページのエントリポイント。 // // SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only import { register } from "../lib.ts"; register();
-
-
-
@@ -0,0 +1,13 @@/* ドキュメントページの共通 CSS 。 * * SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> * SPDX-License-Identifier: AGPL-3.0-only */ body { display: flex; flex-direction: column; align-items: start; gap: var(--space-px-2); padding: var(--space-px-4); }
-
-
packages/gui/src/all.css (new)
-
@@ -0,0 +1,14 @@/* @yamori/gui の全てのスタイル。 * JS でテーマを動的に変える場合は個別に CSS を読み込むこと。 * * SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> * SPDX-License-Identifier: AGPL-3.0-only */ @import "./vars/base.css"; @import "./vars/dark.css" (prefers-color-scheme: dark); @import "./vars/contrast.css" (prefers-contrast: more); @import "./vars/dark-contrast.css" (prefers-color-scheme: dark) and (prefers-contrast: more); @import "./global.css";
-
-
-
@@ -0,0 +1,77 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only export interface UIComponent< TagName extends string, Element extends CustomElementConstructor, > { readonly tagName: TagName; readonly constructor: Element; register(): boolean; } /** * CustomElement を使いやすい I/F にラップする。 * 渡された CustomElement は自動的に `nonUAMixin` が適用され、 * `:is(:state(nonua), :--nonua, [nonua])` でクエリできるようになる。 */ export function component< TagName extends string, Element extends CustomElementConstructor, >(tagName: TagName, constructor: Element): UIComponent<TagName, Element> { return { tagName, constructor, register() { if (customElements.get(tagName)) { return false; } customElements.define(tagName, nonUAMixin(constructor)); return true; }, }; } /** * Custom Element を "nonua" というキーワードで CSS からセレクトできるようにする mixin 。 * * CSS では未だにビルトインのタグとユーザ定義のタグをセレクトできず、また UA のスタイルを * 無効化する仕様も存在しない。そのため、UA が勝手に定義したスタイルを削除するためには、 * 1. Custom Element に識別用のマーカーを設定 * 2. マーカーのついていないものを全てリセット * とするしかない。この手法だと 3rd party の要素までもリセットされてしまうが、 * このプロジェクトにおいては 3rd party の Custom Element は利用しないためこの手法で * 問題はない。 * * 付与するマーカーは以下の通り: * * A) CustomStateSet の `nonua` ステート (Evergreen Browser) * B) CustomStateSet の `--nonua` ステート (Chromium v90 <= x < v125) * C) `nonua` 属性 * * 全てのマーカーに対応するには以下のようなセレクタが必要。 * * ```css * :is(:state(nonua), :--nonua, [nonua]) * ``` */ function nonUAMixin<T extends CustomElementConstructor>(ctor: T): T { return class WithNonUA extends ctor { constructor(...params: any[]) { super(...params); const internals = this.attachInternals(); if (internals && internals.states) { // https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet#compatibility_with_dashed-ident_syntax try { internals.states.add("nonua"); } catch { internals.states.add("--nonua"); } } else { this.setAttribute("nonua", ""); } } }; }
-
-
-
@@ -0,0 +1,23 @@/* * SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> * SPDX-License-Identifier: AGPL-3.0-only */ :host { display: flex; padding: var(--space-px-3); border: 1px solid oklch(var(--color-fg)); background-color: oklch(var(--color-fg)); border-radius: 3px; color: oklch(var(--color-bg)); } :host([inline]) { display: inline-flex; } :host(:focus-visible) { border-color: tomato; outline: none; }
-
-
-
@@ -0,0 +1,34 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only import { component } from "../_utils"; import css from "./button.css?inline"; /** * 表示サンプル用コンポーネント。 */ export const YamoriButton = component( "yamori-button", class extends HTMLElement { constructor() { super(); const shadow = this.attachShadow({ mode: "open", }); const style = document.createElement("style"); style.textContent = css; shadow.appendChild(style); const span = document.createElement("span"); shadow.appendChild(span); const slot = document.createElement("slot"); span.appendChild(slot); this.tabIndex = 0; } }, );
-
-
-
@@ -0,0 +1,15 @@/* @yamori/gui を利用するページで読み込む必要のあるカプセル化されていないスタイル。 * * SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> * SPDX-License-Identifier: AGPL-3.0-only */ @import "./reset.css"; body { font-size: 1rem; line-height: 1.25; background-color: oklch(var(--color-bg)); color: oklch(var(--color-fg)); }
-
-
-
@@ -0,0 +1,16 @@<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>パッケージドキュメント | Yamori GUI</title> <link rel="stylesheet" href="./all.css" /> <script type="module" async src="./_docs/register.ts"></script> <link rel="stylesheet" href="./_docs/styles.css" /> </head> <body> <yamori-button inline>ボタン</yamori-button> <yamori-button>Button</yamori-button> <button>Normal Button</button> </body> </html>
-
-
packages/gui/src/lib.ts (new)
-
@@ -0,0 +1,10 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only import { YamoriButton } from "./components/button/button.ts"; export { YamoriButton }; export function register(): void { YamoriButton.register(); }
-
-
-
@@ -0,0 +1,11 @@/* 究極のリセット CSS 。 * * SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> * SPDX-License-Identifier: AGPL-3.0-only */ /* UA スタイルを全部消す */ :where(body, body :not(svg, svg *, :is([nonua], :--nonua, :state(nonua)))) { all: unset; box-sizing: border-box; }
-
-
-
@@ -0,0 +1,27 @@/* @yamori/gui で利用されるグローバルな CSS カスタムプロパティの全定義。 * * SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> * SPDX-License-Identifier: AGPL-3.0-only */ :root, :host { --font-sans: "Inter Variable", sans-serif; /* Plastic Ratio の近似値 */ --scale: calc(53 / 40); --hue: 260deg; --chroma: 3%; --color-bg-l: 99%; --color-bg: var(--color-bg-l) var(--chroma) var(--hue); --color-fg-l: 25%; --color-fg: var(--color-fg-l) var(--chroma) var(--hue); --space-px-1: 2px; --space-px-2: calc(var(--space-px-1) * var(--scale)); --space-px-3: calc(var(--space-px-2) * var(--scale)); --space-px-4: calc(var(--space-px-3) * var(--scale)); }
-
-
-
@@ -0,0 +1,11 @@/* コントラスト強調時に上書きするカスタムプロパティ。 * base.css の後に読み込まれる必要がある。 * * SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> * SPDX-License-Identifier: AGPL-3.0-only */ :root, :host { --color-fg-l: 1%; }
-
-
-
@@ -0,0 +1,12 @@/* ダークモード有効且つコントラスト強調時に上書きするカスタムプロパティ。 * base.css の後に読み込まれる必要がある。 * * SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> * SPDX-License-Identifier: AGPL-3.0-only */ :root, :host { --color-bg-l: 10%; --color-fg-l: 99%; }
-
-
-
@@ -0,0 +1,12 @@/* ダークモード有効時に上書きするカスタムプロパティ。 * base.css の後に読み込まれる必要がある。 * * SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> * SPDX-License-Identifier: AGPL-3.0-only */ :root, :host { --color-bg-l: 15%; --color-fg-l: 95%; }
-
-
-
@@ -0,0 +1,15 @@// types/*.d.ts をビルドする際の設定。 // // SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only { "extends": "./tsconfig.json", "compilerOptions": { "noEmit": false, "declaration": true, "emitDeclarationOnly": true, "outDir": "./types" }, "include": ["src/**/*.ts"], "exclude": ["src/_docs/*.ts"] }
-
-
-
@@ -0,0 +1,10 @@{ "extends": "../../tsconfig.jsonc", "compilerOptions": { "moduleResolution": "Bundler", "noEmit": true, "lib": ["ES2020", "DOM", "DOM.iterable"], "types": ["vite/client"] }, "include": ["*.ts", "src/**/*.ts"] }
-
-
-
@@ -0,0 +1,9 @@TypeScript のコンパイラ設定ファイル。 実際は JSON ではなく JSONC のためコメントを含められるが、 .jsonc にすると TypeScript 関連のツールがデフォルトで読まなくなる。逆に .json のまま コメントを含めるとフォーマッタといった TypeScript 以外の JSON を扱うツール でエラーになる。そのため拡張子と仕様に準拠している。 SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> SPDX-License-Identifier: AGPL-3.0-only
-
-
-
@@ -0,0 +1,87 @@// Web 向けのバンドラー、 Vite の設定ファイル。 // <https://vite.dev/> // // SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only import { readdir } from "node:fs/promises"; import { extname, relative } from "node:path"; import { defineConfig, type UserConfig, type Rollup } from "vite"; const srcDir = new URL("./src/", import.meta.url); async function htmlFilesToRollupInput(): Promise<Rollup.InputOption> { const files = await readdir(srcDir, { recursive: true }); return files .filter((file) => extname(file) === ".html") .map((file) => new URL(file, srcDir).pathname); } function libraryCSSFiles( files: readonly URL[], ): Extract<Rollup.InputOption, Record<string, string>> { const entries: ReturnType<typeof libraryCSSFiles> = {}; for (const file of files) { const displayPath = relative(process.cwd(), file.pathname); if (!file.toString().startsWith(srcDir.toString())) { console.warn( `You cannot specify CSS file outside src/ directory as library output: ${displayPath}`, ); continue; } if (!file.pathname.endsWith(".css")) { console.warn( `You cannot pass non-CSS file to libraryCSSFiles function in Vite config: ${displayPath}`, ); continue; } const name = file.toString().slice(srcDir.toString().length, -4); entries[name] = file.pathname; } return entries; } export default defineConfig(async () => { return { root: srcDir.pathname, build: // Vite はライブラリと通常の HTML 出力を同時に行えない仕様バグがあるため、 // 環境変数でビルド対象を切り分けて複数回実行することで両方ビルドしている。 process.env.TARGET === "lib" ? { emptyOutDir: true, outDir: "../dist", cssCodeSplit: true, lib: { entry: { ...libraryCSSFiles([ new URL("./src/all.css", import.meta.url), new URL("./src/reset.css", import.meta.url), new URL("./src/global.css", import.meta.url), new URL("./src/vars/base.css", import.meta.url), new URL("./src/vars/dark.css", import.meta.url), new URL("./src/vars/contrast.css", import.meta.url), new URL("./src/vars/dark-contrast.css", import.meta.url), ]), lib: new URL("./lib.ts", srcDir).pathname, }, formats: ["es"], }, } : { emptyOutDir: true, outDir: "../docs-dist", rollupOptions: { input: await htmlFilesToRollupInput(), }, }, } satisfies UserConfig; });
-
-
-
@@ -1,19 +1,9 @@{ "extends": "../../tsconfig.jsonc", "compilerOptions": { "exactOptionalPropertyTypes": true, "noImplicitOverride": true, "noUncheckedIndexedAccess": true, "noUnusedLocals": true, "noUnusedParameters": true, "strict": true, "allowImportingTsExtensions": true, "skipLibCheck": true, "allowArbitraryExtensions": true, "module": "ES2022", "moduleResolution": "Bundler", "noEmit": true, "lib": ["ES2020", "DOM"], "target": "ES2020", "paths": { "@/*": ["./src/*"] }
-
-
tsconfig.jsonc (new)
-
@@ -0,0 +1,23 @@// プロジェクト共通のTypeScript のコンパイラ設定。 // // SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only { "compilerOptions": { // Type Checking "exactOptionalPropertyTypes": true, "noImplicitOverride": true, "noUncheckedIndexedAccess": true, "noUnusedLocals": true, "noUnusedParameters": true, "strict": true, // Modules "allowArbitraryExtensions": true, "allowImportingTsExtensions": true, "module": "ES2022", // Language and Environment "target": "ES2020", // Completeness "skipLibCheck": true } }
-