@iyulab/house-style 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,36 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] - 2026-10-05
4
+
5
+ First release as a package. Until now `@iyulab/house-style` was only the guide site.
6
+
7
+ ### Added
8
+
9
+ - **Speaks `@iyulab/components` 2.0's appearance vocabulary** — the guide, the reference app and
10
+ the example use `appearance` (`plain` for the former `ghost`, `soft` for `filled`), status tags
11
+ are `soft` with a `dot`, `color="neutral"` is the grey label. Text fields get the same three
12
+ heights as buttons (28 / 32 / 40) through `size`.
13
+
14
+ - **The house theme, CSS only, in the `iyu.house` cascade layer** (requires `@iyulab/components`
15
+ 2.0, whose defaults sit in `iyu.base`; unlayered application CSS always wins):
16
+ - `styles/tokens.css` — warm-grey neutrals, ink primary action, blue reserved for links and
17
+ focus (`--u-focus-ring-color`), status tints with AA text, canvas vs surface
18
+ (`--u-canvas-bg-color`), radius 4/6/10, a flat surface lift and one overlay shadow,
19
+ Pretendard and the 22/18/15/14/13/12/11 type ladder — light and dark. Brand tokens
20
+ `--hs-brand`, `--hs-brand-soft`, `--hs-brand-ink` (neutral by default).
21
+ - `styles/components.css` — 28/32/40 buttons, flat cards, side-sheet header and visible body
22
+ scrollbar, 40px `flex-table` rows with a quiet header and no zebra, page header eyebrow and
23
+ description, the shell's canvas and page padding.
24
+ - `styles/patterns.css` — `hs-*` patterns: page head, saved-view tabs with counts, filter bar,
25
+ stat strip, table card with selection toolbar and pager, plain table with row actions on
26
+ hover, card and description list, two-column detail, callout (info · success · warning ·
27
+ danger · note), form sections and fields, side-sheet footer, attribute label, days-left mark.
28
+ - `styles/tailwind.css` — the Tailwind CSS v4 entry: imports the theme into Tailwind's
29
+ `components` layer (Tailwind's preflight would otherwise beat the house layer) and maps the
30
+ roles to utilities. `styles/tailwind-neutrals.css` (opt-in) points Tailwind's grey ramps at the
31
+ house neutrals.
32
+ - `styles/fonts.css` — Pretendard Variable, dynamic subset.
33
+ - Absorbs `@iyulab/enterprise/styles/preset.css`: the type scale, radius and elevation values now
34
+ live here with the rest of the house values (enterprise drops its copy in its next minor).
35
+ - `npm test` — token contract: every light colour has a dark value (the house layer would
36
+ otherwise leak a light value into dark mode) and the promised contrast pairs hold in both modes.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 iyulab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # House Style
2
+
3
+ The iyulab house theme for `@iyulab/components`, `@iyulab/modern-app` and `@iyulab/flex-table`,
4
+ and the guide that documents it.
5
+
6
+ - **The theme** (`@iyulab/house-style` on npm) is CSS only: house token values, component
7
+ recipes, line-of-business layout patterns and a Tailwind CSS v4 preset. One import turns the
8
+ neutral component defaults into a finished, quiet, dense business UI.
9
+ - **The guide** (the website below) shows every value read live from the theme it loads, so it
10
+ cannot drift from the files an application imports.
11
+
12
+ ## Use the theme
13
+
14
+ ```bash
15
+ npm install @iyulab/house-style
16
+ ```
17
+
18
+ ```ts
19
+ import '@iyulab/house-style'; // fonts + tokens + component recipes + layout patterns
20
+ ```
21
+
22
+ **A Tailwind CSS v4 app** imports the Tailwind entry instead, from its Tailwind stylesheet — not
23
+ `@iyulab/house-style` from JS:
24
+
25
+ ```css
26
+ @import 'tailwindcss';
27
+ @import '@iyulab/house-style/styles/tailwind.css'; /* theme + role utilities */
28
+ @import '@iyulab/house-style/styles/tailwind-neutrals.css'; /* optional: gray-*/slate-* follow the theme */
29
+ ```
30
+
31
+ Tailwind declares its own layers (`theme, base, components, utilities`) after the house layers,
32
+ so its preflight would beat the theme; `tailwind.css` imports the theme into Tailwind's
33
+ `components` layer — above preflight, below utilities and your own CSS.
34
+
35
+ | File | What it holds |
36
+ |---|---|
37
+ | `styles/index.css` (package root) | everything below plus Pretendard — for apps without Tailwind |
38
+ | `styles/theme.css` | the theme without fonts (bring your own face) |
39
+ | `styles/tokens.css` | house values for the `--u-*` token axes, light and dark; brand tokens `--hs-brand*` |
40
+ | `styles/components.css` | sizes and finish for `u-button`, `u-card`, `u-drawer`, `flex-table`, the app shell |
41
+ | `styles/patterns.css` | `hs-*` layout patterns — page head, saved views, filter bar, stat strip, table card, callout, form sections, side-sheet footer |
42
+ | `styles/tailwind.css` | the Tailwind v4 entry: fonts + theme (in Tailwind's `components` layer) + role utilities — `bg-canvas`, `text-ink-weak`, `border-line`, `bg-warning-soft`, `rounded-card` … |
43
+ | `styles/tailwind-neutrals.css` | optional: Tailwind's grey ramps (`gray`, `slate`, `zinc`, `neutral`, `stone`) follow the house neutrals |
44
+ | `styles/fonts.css` | Pretendard Variable (dynamic subset) |
45
+
46
+ ### Cascade layers — your CSS always wins
47
+
48
+ ```
49
+ @layer iyu.base, iyu.house;
50
+ iyu.base @iyulab/components built-in defaults
51
+ iyu.house this theme
52
+ (unlayered) your application
53
+ ```
54
+
55
+ Requires `@iyulab/components` 2.0 or later (the release that put its defaults in `iyu.base`).
56
+ Because the application is unlayered, a plain `:root { … }` rule overrides the theme — no
57
+ specificity tricks, no load-order rules. Scope a mode-specific value with `:root[theme="dark"]`.
58
+
59
+ ### Brand it
60
+
61
+ The house is neutral on purpose. An application sets its identity colour with three tokens and
62
+ keeps it out of buttons and warnings (a red brand next to a red "danger" reads as one colour):
63
+
64
+ ```css
65
+ :root {
66
+ --hs-brand: #c8161d; /* logo mark */
67
+ --hs-brand-soft: #fcebec; /* current navigation item */
68
+ --hs-brand-ink: #a3121a; /* its text */
69
+ }
70
+ :root[theme="dark"] {
71
+ --hs-brand-soft: #3a1a1c;
72
+ --hs-brand-ink: #ff9a9e;
73
+ }
74
+ ```
75
+
76
+ Everything else — warm-grey neutrals, the ink primary action, blue for links and focus, status
77
+ tints, the 22/18/15/14/13/12/11 type ladder, 28/32/40 controls, 40px table rows — comes from the
78
+ theme and can be overridden token by token.
79
+
80
+ ## The guide
81
+
82
+ **Live**: https://iyulab.github.io/house-style/ — this is the guide. There is no
83
+ separate docs site; the page above is the only place to read it.
84
+
85
+ The live page is organized into sections:
86
+
87
+ - **Visual identity & tokens** — type scale, colour roles, surfaces and ink, chart colours, radius, elevation
88
+ - **Layout & viewport** — the responsive sidebar shell, nav/routes, breakpoints
89
+ - **Component depth** — surfaces and controls at the standard radius and elevation
90
+ - **Data patterns** — how tabular/record data is presented
91
+ - **Feedback & motion** — alerts, progress, and waiting states
92
+ - **User flows** — CRUD, wizard, and bulk-action patterns
93
+ - **Voice, tone & accessibility** — focus visibility and keyboard interaction
94
+
95
+ ## The reference app
96
+
97
+ The guide has a second half: a small but working line-of-business app, assembled only from
98
+ these libraries, at **https://iyulab.github.io/house-style/app/** (sign in with `demo` / `demo`
99
+ — the backend is mocked in the browser, there are no real accounts). It is reachable from the
100
+ guide's sidebar under "Open the reference app".
101
+
102
+ Where the guide shows each pattern on its own, the app shows them load-bearing: a sidebar shell,
103
+ a list screen with filtering, selection and bulk actions, a master-detail order screen with an
104
+ edit drawer, and a multi-step wizard. It is held to the guide rather than kept beside it — a
105
+ check in this monorepo compares the two and reports any layout primitive one half uses and the
106
+ other does not.
107
+
108
+ Sections that say "Not yet decided" mean exactly that — no design decision has
109
+ been made for that area yet, rather than the guide inventing one.
110
+
111
+ ## Local development
112
+
113
+ From the `node-packages` monorepo root (never from inside this directory):
114
+
115
+ ```bash
116
+ npm install
117
+ npm run start -w @iyulab/house-style
118
+ ```
119
+
120
+ The landing page's "Start here" path is checked end to end: a fresh `create-vite` project, the
121
+ published packages, the page's own `index.html` / `main.ts` / recipe files, a production build,
122
+ and a browser that must find table rows on `/`. The deploy workflow runs it before publishing the
123
+ site; run it locally with (needs network and Playwright's Chromium):
124
+
125
+ ```bash
126
+ npm run check:start-here -w @iyulab/house-style
127
+ ```
128
+
129
+ ## License
130
+
131
+ MIT
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@iyulab/house-style",
3
+ "version": "0.1.0",
4
+ "description": "The iyulab house theme: a CSS-only theme for @iyulab components — tokens, component recipes, line-of-business layout patterns and a Tailwind CSS v4 preset — plus the guide site that documents it.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/iyulab/house-style.git"
9
+ },
10
+ "type": "module",
11
+ "style": "./styles/index.css",
12
+ "exports": {
13
+ ".": "./styles/index.css",
14
+ "./styles/*": "./styles/*",
15
+ "./package.json": "./package.json"
16
+ },
17
+ "files": [
18
+ "styles",
19
+ "README.md",
20
+ "CHANGELOG.md",
21
+ "LICENSE"
22
+ ],
23
+ "sideEffects": [
24
+ "**/*.css"
25
+ ],
26
+ "scripts": {
27
+ "preversion": "node -e \"if(require('fs').existsSync('../../scripts/preversion-check.mjs'))require('child_process').execFileSync('node',['../../scripts/preversion-check.mjs'],{stdio:'inherit'})\"",
28
+ "start": "vite",
29
+ "build": "npm run typecheck && vite build",
30
+ "preview": "vite preview",
31
+ "typecheck": "tsc --noEmit",
32
+ "check:start-here": "node scripts/check-start-here.mjs",
33
+ "test": "node --test \"tests/*.test.mjs\""
34
+ },
35
+ "engines": {
36
+ "node": ">=20"
37
+ },
38
+ "dependencies": {
39
+ "pretendard": "^1.3.9"
40
+ },
41
+ "peerDependencies": {
42
+ "@iyulab/components": "^2.0.0",
43
+ "@iyulab/flex-table": ">=0.41.0",
44
+ "@iyulab/modern-app": ">=0.34.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "@iyulab/flex-table": {
48
+ "optional": true
49
+ },
50
+ "@iyulab/modern-app": {
51
+ "optional": true
52
+ }
53
+ },
54
+ "devDependencies": {
55
+ "@iyulab/components": "^2.0.0",
56
+ "@iyulab/data-components": "^0.33.0",
57
+ "@iyulab/enterprise": "^0.22.0",
58
+ "@iyulab/modern-app": "^0.34.0",
59
+ "@iyulab/router": "^0.19.0",
60
+ "@iyulab/u-widgets": "^0.24.0",
61
+ "@lit/react": "^1.0.8",
62
+ "@types/node": "^26.1.1",
63
+ "@types/react": "^19.2.14",
64
+ "@types/react-dom": "^19.2.3",
65
+ "echarts": "^6.1.0",
66
+ "lit": "^3.3.2",
67
+ "msw": "^3.0.0",
68
+ "playwright": "^1.62.1",
69
+ "react": "^19.2.7",
70
+ "react-dom": "^19.2.7",
71
+ "typescript": "^6.0.2",
72
+ "vite": "^8.0.3"
73
+ },
74
+ "msw": {
75
+ "workerDirectory": [
76
+ "public"
77
+ ]
78
+ }
79
+ }
@@ -0,0 +1,125 @@
1
+ /*
2
+ * @iyulab/house-style — component recipes
3
+ * ==========================================================================
4
+ * Element-level house defaults for `@iyulab/*` components, written only against their public
5
+ * surface: component custom properties (`--btn-*`, `--ft-*`, `--card-*`), `::part()` and the host.
6
+ * Nothing here changes what an attribute means — an `appearance="solid"` button stays solid; the
7
+ * house only sets size, density and finish.
8
+ *
9
+ * Everything sits in `@layer iyu.house`, so an application rule always wins. Rules aimed at a
10
+ * host beat the component's own `:host` styles (outer context wins over shadow context).
11
+ * ========================================================================== */
12
+
13
+ @layer iyu.base, iyu.house;
14
+
15
+ @layer iyu.house {
16
+
17
+ /* ── Body ──────────────────────────────────────────────────────────────── */
18
+ :root {
19
+ font-family: var(--u-font-base);
20
+ font-variant-numeric: tabular-nums; /* amounts, counts, dates line up everywhere */
21
+ color: var(--u-txt-color);
22
+ -webkit-font-smoothing: antialiased;
23
+ }
24
+
25
+ /* ── Button — 28 / 32 / 40, medium weight ─────────────────────────────── *
26
+ * Height follows the components' own formula (1.5em line + 2 × padding + 2px border), so
27
+ * these are padding values, not fixed heights: text and icon-only buttons stay equal. */
28
+ u-button:not([size]),
29
+ u-button[size='md'] {
30
+ font-size: 13px;
31
+ --btn-padding-block: 5.25px; /* 19.5 + 10.5 + 2 = 32 */
32
+ --btn-padding-inline: 12px;
33
+ }
34
+ u-button[size='sm'] {
35
+ --btn-padding-block: 4px; /* 12px → 18 + 8 + 2 = 28 */
36
+ --btn-padding-inline: 8px;
37
+ }
38
+ u-button[size='lg'] {
39
+ font-size: 14px;
40
+ --btn-padding-block: 8.5px; /* 21 + 17 + 2 = 40 */
41
+ --btn-padding-inline: 16px;
42
+ }
43
+ u-button[appearance='link'] {
44
+ --btn-padding-inline: 0;
45
+ }
46
+
47
+ /* ── Text fields — the same three heights as buttons: 28 / 32 / 40.
48
+ * (Components 2.0 renders fields at 14px by default, so a field no longer grows with the
49
+ * surrounding text; the house sets the box padding per size.) ── */
50
+ :is(u-input, u-select, u-textarea, u-date-picker, u-date-range-picker)[size='sm'] {
51
+ --u-field-padding-block: 4px; /* 12px → 18 + 8 + 2 = 28 */
52
+ }
53
+ :is(u-input, u-select, u-textarea, u-date-picker, u-date-range-picker)[size='lg'] {
54
+ font-size: 14px;
55
+ --u-field-padding-block: 8.5px; /* 21 + 17 + 2 = 40 */
56
+ }
57
+
58
+ /* ── Tag — 22px labels (12px text · 1px block padding · hairline) to sit inside 40px rows.
59
+ * A status label is the default `soft` appearance (tint, no outline) with `dot`. ── */
60
+ u-tag {
61
+ --tag-padding-block: 1px;
62
+ --tag-padding-inline: 8px;
63
+ --tag-gap: 5px;
64
+ }
65
+
66
+ /* ── Card — flat surface, hairline border; shadow is for things that float ── */
67
+ u-card {
68
+ --card-border-color: var(--u-border-color);
69
+ }
70
+
71
+ /* ── Drawer — the side sheet. Header and footer separated by a hairline,
72
+ * body scrollbar always visible so long forms show there is more below. ── */
73
+ u-drawer::part(header) {
74
+ border-bottom: 1px solid var(--u-border-color);
75
+ }
76
+ u-drawer::part(body) {
77
+ scrollbar-gutter: stable;
78
+ scrollbar-width: thin;
79
+ scrollbar-color: var(--u-border-color-strong) transparent;
80
+ }
81
+
82
+ /* ── Data table (`@iyulab/flex-table`) — 40px rows, 13px cells, quiet header,
83
+ * no zebra (hover and selection carry the row), tabular figures. ──────── */
84
+ flex-table {
85
+ --ft-font-family: var(--u-font-base);
86
+ --ft-font-size: 13px;
87
+ --ft-row-height: var(--hs-row);
88
+ --ft-cell-padding-inline: 12px;
89
+ --ft-header-font-size: 12px;
90
+ --ft-header-font-weight: 500;
91
+ --ft-header-text-color: var(--u-txt-color-weak);
92
+ --ft-header-bg: var(--u-bg-color-raised);
93
+ --ft-row-odd-bg: var(--u-bg-color);
94
+ --ft-row-odd-hover-bg: var(--u-bg-color-hover);
95
+ --ft-border-color: var(--u-border-color);
96
+ --ft-selection-bg: var(--u-primary-bg-color);
97
+ }
98
+ flex-table.hs-compact,
99
+ .hs-compact flex-table {
100
+ --ft-row-height: var(--hs-row-compact);
101
+ }
102
+
103
+ /* ── Page header (`@iyulab/modern-app`) — eyebrow · 22px title · one-line description ── */
104
+ u-page-header::part(eyebrow) {
105
+ font-size: var(--u-text-label-size);
106
+ color: var(--u-txt-color-weak);
107
+ }
108
+ u-page-header::part(subtitle) {
109
+ font-size: var(--u-text-label-size);
110
+ color: var(--u-txt-color-weak);
111
+ }
112
+
113
+ /* ── Sidebar layout — the sidebar is a surface separated from the canvas by a hairline.
114
+ * (The page already sits on `--u-canvas-bg-color`; padding is `--app-main-padding`.) ── */
115
+ u-sidebar-layout::part(sidebar) {
116
+ border-inline-end: 1px solid var(--u-border-color);
117
+ }
118
+ @media print {
119
+ u-sidebar-layout::part(main) {
120
+ background: none;
121
+ padding: 0;
122
+ }
123
+ }
124
+
125
+ } /* @layer iyu.house */
@@ -0,0 +1,11 @@
1
+ /*
2
+ * @iyulab/house-style — fonts
3
+ * ==========================================================================
4
+ * Pretendard Variable, dynamic subset: the browser downloads only the glyph ranges a page
5
+ * uses. `--u-font-base` (tokens.css) lists it first and falls back to the platform's Korean
6
+ * and system fonts when it is not loaded.
7
+ *
8
+ * Kept separate from the theme so an application that already ships Pretendard (or another
9
+ * face) can skip it: import `styles/theme.css` instead of `styles/index.css`.
10
+ * ========================================================================== */
11
+ @import 'pretendard/dist/web/variable/pretendardvariable-dynamic-subset.css';
@@ -0,0 +1,11 @@
1
+ /*
2
+ * @iyulab/house-style — everything: fonts + tokens + component recipes + patterns
3
+ *
4
+ * import '@iyulab/house-style'; // same file, via the package "style"/"." entry
5
+ * import '@iyulab/house-style/styles/index.css';
6
+ *
7
+ * 🔴A Tailwind CSS v4 app imports `styles/tailwind.css` from its Tailwind entry INSTEAD of this
8
+ * file — Tailwind's preflight layer would otherwise beat the theme (see tailwind.css).
9
+ */
10
+ @import './fonts.css';
11
+ @import './theme.css';