@operato/styles 10.23.0 → 10.29.9

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 CHANGED
@@ -3,6 +3,32 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ### [10.29.9](https://github.com/hatiolab/operato/compare/v10.29.8...v10.29.9) (2026-10-05)
7
+
8
+
9
+ ### :rocket: New Features
10
+
11
+ * **styles:** page frames lay a ground, fill the window, and share button and field sizes ([47c625b](https://github.com/hatiolab/operato/commit/47c625bbb1167551d4c2eae1354722590ebd81b1))
12
+
13
+
14
+
15
+ ### [10.29.1](https://github.com/hatiolab/operato/compare/v11.0.0...v10.29.1) (2026-09-26)
16
+
17
+
18
+ ### :mega: Other
19
+
20
+ * **release:** return the version to 10.29.0 -- 11.0.0 was a mistaken major bump ([6ef39c1](https://github.com/hatiolab/operato/commit/6ef39c1107f0a6614ac78255fdb700e3eecfd0f7))
21
+
22
+
23
+
24
+ ## [11.0.0](https://github.com/hatiolab/operato/compare/v10.29.0...v11.0.0) (2026-09-26)
25
+
26
+ **Note:** Version bump only for package @operato/styles
27
+
28
+
29
+
30
+
31
+
6
32
  ## [10.23.0](https://github.com/hatiolab/operato/compare/v10.22.0...v10.23.0) (2026-09-24)
7
33
 
8
34
 
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Buttons in a page body, a section header, at a row's end or in a popup — the same look as the
3
+ * page action bar (ButtonContainerStyles), so a button reads the same wherever it sits
4
+ * (operato-application/design/ui/page-standard.md §8).
5
+ *
6
+ * ── Why not Material buttons ─────────────────────────────────────────────
7
+ * Raised to the 32px token, a Material button still keeps a 14px/500 label, a 64px minimum width and
8
+ * a pill shape. Among 13px body text it looks larger and softer than everything around it, and in a
9
+ * form grid it stretched to the column (a two-letter "add" measured 192px). The action bar dropped
10
+ * Material for the same reason in 2026-08; the page body follows it.
11
+ *
12
+ * ── Four kinds, one size per place ───────────────────────────────────────
13
+ * <button> outlined, the default
14
+ * <button raised> filled with the accent — one per place
15
+ * <button quiet> text only: "today", "more"
16
+ * <button danger> cannot be undone — red text
17
+ * <button small> inside a table row or cell (the cell height)
18
+ * <button icon-only> square; give it a title and an aria-label
19
+ *
20
+ * A button is as wide as its label; it never stretches to a grid track.
21
+ *
22
+ * Everything is under :where(), zero specificity: a screen's own rule for a special button still wins.
23
+ * ButtonContainerStyles is left byte for byte as it was — dssp draws 26 of its own pages with it.
24
+ */
25
+ export declare const ButtonStyles: import("lit").CSSResult;
@@ -0,0 +1,127 @@
1
+ import { css } from 'lit';
2
+ import { actionHeight } from './action-heights.js';
3
+ /**
4
+ * Buttons in a page body, a section header, at a row's end or in a popup — the same look as the
5
+ * page action bar (ButtonContainerStyles), so a button reads the same wherever it sits
6
+ * (operato-application/design/ui/page-standard.md §8).
7
+ *
8
+ * ── Why not Material buttons ─────────────────────────────────────────────
9
+ * Raised to the 32px token, a Material button still keeps a 14px/500 label, a 64px minimum width and
10
+ * a pill shape. Among 13px body text it looks larger and softer than everything around it, and in a
11
+ * form grid it stretched to the column (a two-letter "add" measured 192px). The action bar dropped
12
+ * Material for the same reason in 2026-08; the page body follows it.
13
+ *
14
+ * ── Four kinds, one size per place ───────────────────────────────────────
15
+ * <button> outlined, the default
16
+ * <button raised> filled with the accent — one per place
17
+ * <button quiet> text only: "today", "more"
18
+ * <button danger> cannot be undone — red text
19
+ * <button small> inside a table row or cell (the cell height)
20
+ * <button icon-only> square; give it a title and an aria-label
21
+ *
22
+ * A button is as wide as its label; it never stretches to a grid track.
23
+ *
24
+ * Everything is under :where(), zero specificity: a screen's own rule for a special button still wins.
25
+ * ButtonContainerStyles is left byte for byte as it was — dssp draws 26 of its own pages with it.
26
+ */
27
+ export const ButtonStyles = css `
28
+ :where(button:not([unstyled])) {
29
+ display: inline-flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ gap: var(--spacing-small, 4px);
33
+ justify-self: start;
34
+ align-self: center;
35
+ flex: none;
36
+
37
+ box-sizing: border-box;
38
+ height: ${actionHeight('--context-action-height')};
39
+ padding: 0 var(--spacing-large, 12px);
40
+ border: 1px solid var(--md-sys-color-outline-variant);
41
+ border-radius: var(--button-border-radius, var(--md-sys-shape-corner-small));
42
+
43
+ background-color: var(--md-sys-color-surface-container-lowest);
44
+ color: var(--md-sys-color-on-surface);
45
+
46
+ font-family: inherit;
47
+ font-size: var(--ui-t-body, 13px);
48
+ /* The weight is what tells a button from a sentence at the same size. */
49
+ font-weight: 600;
50
+ line-height: 1;
51
+ white-space: nowrap;
52
+
53
+ cursor: pointer;
54
+ transition:
55
+ background-color 0.12s,
56
+ border-color 0.12s,
57
+ color 0.12s;
58
+ }
59
+
60
+ :where(button:not([unstyled])) md-icon {
61
+ --md-icon-size: 16px;
62
+ flex: none;
63
+ color: var(--md-sys-color-primary);
64
+ }
65
+
66
+ :where(button:not([unstyled]):hover:not(:disabled)) {
67
+ background-color: var(--md-sys-color-secondary-container);
68
+ color: var(--md-sys-color-on-secondary-container);
69
+ border-color: var(--md-sys-color-secondary);
70
+ }
71
+
72
+ :where(button:not([unstyled]):focus-visible) {
73
+ outline: 2px solid var(--md-sys-color-primary);
74
+ outline-offset: 2px;
75
+ }
76
+
77
+ :where(button:not([unstyled]):disabled) {
78
+ opacity: 0.38;
79
+ cursor: default;
80
+ }
81
+
82
+ :where(button[raised]) {
83
+ background-color: var(--md-sys-color-primary);
84
+ border-color: var(--md-sys-color-primary);
85
+ color: var(--md-sys-color-on-primary);
86
+ }
87
+
88
+ :where(button[raised]) md-icon {
89
+ color: var(--md-sys-color-on-primary);
90
+ }
91
+
92
+ :where(button[raised]:hover:not(:disabled)) {
93
+ background-color: color-mix(in srgb, var(--md-sys-color-primary) 88%, black);
94
+ border-color: color-mix(in srgb, var(--md-sys-color-primary) 88%, black);
95
+ color: var(--md-sys-color-on-primary);
96
+ }
97
+
98
+ :where(button[quiet]) {
99
+ border-color: transparent;
100
+ background-color: transparent;
101
+ }
102
+
103
+ /* Every rule here weighs the same, so the later one wins: each kind restates its own hover. */
104
+ :where(button[quiet]:hover:not(:disabled)) {
105
+ background-color: var(--md-sys-color-secondary-container);
106
+ }
107
+
108
+ /* Red words, not a red plate: a plate shouts on every row; the words still say "this cannot be undone". */
109
+ :where(button[danger]) {
110
+ color: var(--ui-crit, var(--md-sys-color-error));
111
+ }
112
+
113
+ :where(button[small]) {
114
+ height: ${actionHeight('--cell-action-height')};
115
+ padding: 0 var(--spacing-medium, 8px);
116
+ }
117
+
118
+ :where(button[icon-only]) {
119
+ width: ${actionHeight('--context-action-height')};
120
+ padding: 0;
121
+ }
122
+
123
+ :where(button[icon-only][small]) {
124
+ width: ${actionHeight('--cell-action-height')};
125
+ }
126
+ `;
127
+ //# sourceMappingURL=button-styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button-styles.js","sourceRoot":"","sources":["../../src/button-styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAA;;;;;;;;;;;cAWjB,YAAY,CAAC,yBAAyB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4EvC,YAAY,CAAC,sBAAsB,CAAC;;;;;aAKrC,YAAY,CAAC,yBAAyB,CAAC;;;;;aAKvC,YAAY,CAAC,sBAAsB,CAAC;;CAEhD,CAAA","sourcesContent":["import { css } from 'lit'\n\nimport { actionHeight } from './action-heights.js'\n\n/**\n * Buttons in a page body, a section header, at a row's end or in a popup — the same look as the\n * page action bar (ButtonContainerStyles), so a button reads the same wherever it sits\n * (operato-application/design/ui/page-standard.md §8).\n *\n * ── Why not Material buttons ─────────────────────────────────────────────\n * Raised to the 32px token, a Material button still keeps a 14px/500 label, a 64px minimum width and\n * a pill shape. Among 13px body text it looks larger and softer than everything around it, and in a\n * form grid it stretched to the column (a two-letter \"add\" measured 192px). The action bar dropped\n * Material for the same reason in 2026-08; the page body follows it.\n *\n * ── Four kinds, one size per place ───────────────────────────────────────\n * <button> outlined, the default\n * <button raised> filled with the accent — one per place\n * <button quiet> text only: \"today\", \"more\"\n * <button danger> cannot be undone — red text\n * <button small> inside a table row or cell (the cell height)\n * <button icon-only> square; give it a title and an aria-label\n *\n * A button is as wide as its label; it never stretches to a grid track.\n *\n * Everything is under :where(), zero specificity: a screen's own rule for a special button still wins.\n * ButtonContainerStyles is left byte for byte as it was — dssp draws 26 of its own pages with it.\n */\nexport const ButtonStyles = css`\n :where(button:not([unstyled])) {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--spacing-small, 4px);\n justify-self: start;\n align-self: center;\n flex: none;\n\n box-sizing: border-box;\n height: ${actionHeight('--context-action-height')};\n padding: 0 var(--spacing-large, 12px);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--button-border-radius, var(--md-sys-shape-corner-small));\n\n background-color: var(--md-sys-color-surface-container-lowest);\n color: var(--md-sys-color-on-surface);\n\n font-family: inherit;\n font-size: var(--ui-t-body, 13px);\n /* The weight is what tells a button from a sentence at the same size. */\n font-weight: 600;\n line-height: 1;\n white-space: nowrap;\n\n cursor: pointer;\n transition:\n background-color 0.12s,\n border-color 0.12s,\n color 0.12s;\n }\n\n :where(button:not([unstyled])) md-icon {\n --md-icon-size: 16px;\n flex: none;\n color: var(--md-sys-color-primary);\n }\n\n :where(button:not([unstyled]):hover:not(:disabled)) {\n background-color: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n border-color: var(--md-sys-color-secondary);\n }\n\n :where(button:not([unstyled]):focus-visible) {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: 2px;\n }\n\n :where(button:not([unstyled]):disabled) {\n opacity: 0.38;\n cursor: default;\n }\n\n :where(button[raised]) {\n background-color: var(--md-sys-color-primary);\n border-color: var(--md-sys-color-primary);\n color: var(--md-sys-color-on-primary);\n }\n\n :where(button[raised]) md-icon {\n color: var(--md-sys-color-on-primary);\n }\n\n :where(button[raised]:hover:not(:disabled)) {\n background-color: color-mix(in srgb, var(--md-sys-color-primary) 88%, black);\n border-color: color-mix(in srgb, var(--md-sys-color-primary) 88%, black);\n color: var(--md-sys-color-on-primary);\n }\n\n :where(button[quiet]) {\n border-color: transparent;\n background-color: transparent;\n }\n\n /* Every rule here weighs the same, so the later one wins: each kind restates its own hover. */\n :where(button[quiet]:hover:not(:disabled)) {\n background-color: var(--md-sys-color-secondary-container);\n }\n\n /* Red words, not a red plate: a plate shouts on every row; the words still say \"this cannot be undone\". */\n :where(button[danger]) {\n color: var(--ui-crit, var(--md-sys-color-error));\n }\n\n :where(button[small]) {\n height: ${actionHeight('--cell-action-height')};\n padding: 0 var(--spacing-medium, 8px);\n }\n\n :where(button[icon-only]) {\n width: ${actionHeight('--context-action-height')};\n padding: 0;\n }\n\n :where(button[icon-only][small]) {\n width: ${actionHeight('--cell-action-height')};\n }\n`\n"]}
@@ -49,6 +49,26 @@ export const FormFieldStyles = css `
49
49
  font: var(--input-field-font, inherit);
50
50
  }
51
51
 
52
+ /*
53
+ * 높이 — 한 줄짜리 칸은 높이 토큰 셋 중 하나다(page-standard.md §7). 기본은 보통(폼 · 팝업),
54
+ * 표 안과 필터 막대는 small, 칸이 한두 개인 화면은 large. 단추와 같은 줄에 서면 같은 높이라
55
+ * 윗선과 아랫선이 맞는다. textarea 는 여러 줄이라 높이를 정하지 않는다.
56
+ */
57
+ input:where(:not([type='checkbox'], [type='radio'], [type='range'], [type='color'], [type='file'])),
58
+ select:where(:not([multiple], [size])) {
59
+ height: var(--form-element-height-medium, 30px);
60
+ }
61
+
62
+ input:where([small]),
63
+ select:where([small]) {
64
+ height: var(--form-element-height-small, 24px);
65
+ }
66
+
67
+ input:where([large]),
68
+ select:where([large]) {
69
+ height: var(--form-element-height-large, 40px);
70
+ }
71
+
52
72
  input:where(:not([type='checkbox'], [type='radio']))::placeholder,
53
73
  textarea::placeholder {
54
74
  color: var(--input-field-placeholder-color, var(--md-sys-color-on-surface-variant));
@@ -1 +1 @@
1
- {"version":3,"file":"form-field-styles.js","sourceRoot":"","sources":["../../src/form-field-styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsDjC,CAAA","sourcesContent":["import { css } from 'lit'\n\n/**\n * native 폼 컨트롤(input·select·textarea)의 공용 모양.\n *\n * 왜 필요한가\n * -----------\n * 컴포넌트가 인풋을 shadow DOM 안에 그리므로 테마 CSS(문서 스타일시트)는 그 경계를 넘지 못한다.\n * 경계를 넘는 것은 상속 속성과 CSS 변수뿐이다. 그래서 **값은 전역 토큰 하나**로 두더라도\n * **연결(선언)은 컴포넌트마다** 있어야 한다.\n *\n * 그 연결을 각 컴포넌트가 손으로 쓰면 빠뜨리기 쉽고(실제로 프레임워크 전반에서 배경·글자색이\n * 빠져 브라우저 기본 필드색으로 떨어져 있었다), 같은 화면 안에서도 서로 달라진다.\n * 이 블록을 styles 배열 **앞에** 두면 그 연결이 한 번에 채워진다.\n *\n * static styles = [FormFieldStyles, css`...`]\n *\n * 명시도를 왜 :where() 로 감싸는가\n * --------------------------------\n * 체크박스·라디오를 제외하려면 :not() 이 필요하지만, :not() 은 인자의 명시도를 그대로\n * 더한다. `input:not([type='checkbox']):not([type='radio'])` 는 (0,2,1) 이 되어 컴포넌트가\n * 쓰는 평범한 `input { … }` (0,0,1) 을 **이겨버린다**. 공용 기본값이 지역 조정을 덮어쓰는\n * 셈이라 정반대로 동작한다(실제로 이 때문에 인풋 높이가 납작해지는 일이 있었다).\n *\n * :where() 는 명시도가 0 이므로 `input:where(…)` 는 그냥 (0,0,1) 이다. 결과적으로\n * **같은 명시도 + 나중에 온 선언이 이긴다** — 즉 컴포넌트가 자기 css 를 이 블록 뒤에 두면\n * 원하는 만큼 덮어쓸 수 있다.\n *\n * 조정하는 방법\n * -------------\n * 색·여백·모서리는 --input-field-* 토큰으로 조정한다. 앱 테마에서 한 줄 바꾸면 이 블록을\n * 쓰는 모든 컨트롤에 반영된다. 특정 화면만 다르게 하려면 컴포넌트에서 직접 선언한다.\n *\n * 높이를 맞출 때\n * --------------\n * 다른 shadow root 안의 컨트롤(예: ox-i18n-selector 의 select)은 이 블록이 닿지 않는다.\n * 한 화면에서 높이를 맞추려면 그 컴포넌트가 노출한 토큰에 같은 값을 넘겨야 한다.\n */\nexport const FormFieldStyles = css`\n input:where(:not([type='checkbox'], [type='radio'], [type='range'], [type='color'])),\n select,\n textarea {\n box-sizing: border-box;\n\n background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n border: var(--input-field-border, 1px solid var(--md-sys-color-outline));\n border-radius: var(--input-field-border-radius, var(--md-sys-shape-corner-small, 4px));\n padding: var(--input-field-padding, 6px 9px);\n font: var(--input-field-font, inherit);\n }\n\n input:where(:not([type='checkbox'], [type='radio']))::placeholder,\n textarea::placeholder {\n color: var(--input-field-placeholder-color, var(--md-sys-color-on-surface-variant));\n /* 크기는 값과 같게 둔다 — 작게 만들면 같은 화면에서 필드마다 다른 인상을 준다 */\n opacity: 1;\n }\n\n input:where(:not([type='checkbox'], [type='radio']):focus-visible),\n select:where(:focus-visible),\n textarea:where(:focus-visible) {\n outline: none;\n border-color: var(--input-field-focus-color, var(--md-sys-color-primary));\n }\n\n input:where(:disabled),\n select:where(:disabled),\n textarea:where(:disabled) {\n opacity: var(--input-field-disabled-opacity, 0.5);\n cursor: default;\n }\n\n input:where(:read-only:not([type='checkbox'], [type='radio'])),\n textarea:where(:read-only) {\n background-color: var(--input-field-readonly-background, var(--md-sys-color-surface-container-low));\n }\n\n /* 체크박스·라디오는 모양을 건드리지 않고 색만 테마에 맞춘다 */\n input:where([type='checkbox'], [type='radio']) {\n accent-color: var(--input-field-accent-color, var(--md-sys-color-primary));\n }\n\n /*\n * :invalid 가 아니라 :user-invalid 를 쓴다. :invalid 는 required 빈 필드를 화면이 뜨는\n * 순간부터 붉게 칠하지만, :user-invalid 는 사용자가 입력하거나 제출을 시도한 뒤에만 반응한다.\n */\n input:where(:user-invalid),\n select:where(:user-invalid),\n textarea:where(:user-invalid) {\n border-color: var(--input-field-invalid-color, var(--md-sys-color-error));\n }\n`\n"]}
1
+ {"version":3,"file":"form-field-styles.js","sourceRoot":"","sources":["../../src/form-field-styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0EjC,CAAA","sourcesContent":["import { css } from 'lit'\n\n/**\n * native 폼 컨트롤(input·select·textarea)의 공용 모양.\n *\n * 왜 필요한가\n * -----------\n * 컴포넌트가 인풋을 shadow DOM 안에 그리므로 테마 CSS(문서 스타일시트)는 그 경계를 넘지 못한다.\n * 경계를 넘는 것은 상속 속성과 CSS 변수뿐이다. 그래서 **값은 전역 토큰 하나**로 두더라도\n * **연결(선언)은 컴포넌트마다** 있어야 한다.\n *\n * 그 연결을 각 컴포넌트가 손으로 쓰면 빠뜨리기 쉽고(실제로 프레임워크 전반에서 배경·글자색이\n * 빠져 브라우저 기본 필드색으로 떨어져 있었다), 같은 화면 안에서도 서로 달라진다.\n * 이 블록을 styles 배열 **앞에** 두면 그 연결이 한 번에 채워진다.\n *\n * static styles = [FormFieldStyles, css`...`]\n *\n * 명시도를 왜 :where() 로 감싸는가\n * --------------------------------\n * 체크박스·라디오를 제외하려면 :not() 이 필요하지만, :not() 은 인자의 명시도를 그대로\n * 더한다. `input:not([type='checkbox']):not([type='radio'])` 는 (0,2,1) 이 되어 컴포넌트가\n * 쓰는 평범한 `input { … }` (0,0,1) 을 **이겨버린다**. 공용 기본값이 지역 조정을 덮어쓰는\n * 셈이라 정반대로 동작한다(실제로 이 때문에 인풋 높이가 납작해지는 일이 있었다).\n *\n * :where() 는 명시도가 0 이므로 `input:where(…)` 는 그냥 (0,0,1) 이다. 결과적으로\n * **같은 명시도 + 나중에 온 선언이 이긴다** — 즉 컴포넌트가 자기 css 를 이 블록 뒤에 두면\n * 원하는 만큼 덮어쓸 수 있다.\n *\n * 조정하는 방법\n * -------------\n * 색·여백·모서리는 --input-field-* 토큰으로 조정한다. 앱 테마에서 한 줄 바꾸면 이 블록을\n * 쓰는 모든 컨트롤에 반영된다. 특정 화면만 다르게 하려면 컴포넌트에서 직접 선언한다.\n *\n * 높이를 맞출 때\n * --------------\n * 다른 shadow root 안의 컨트롤(예: ox-i18n-selector 의 select)은 이 블록이 닿지 않는다.\n * 한 화면에서 높이를 맞추려면 그 컴포넌트가 노출한 토큰에 같은 값을 넘겨야 한다.\n */\nexport const FormFieldStyles = css`\n input:where(:not([type='checkbox'], [type='radio'], [type='range'], [type='color'])),\n select,\n textarea {\n box-sizing: border-box;\n\n background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n border: var(--input-field-border, 1px solid var(--md-sys-color-outline));\n border-radius: var(--input-field-border-radius, var(--md-sys-shape-corner-small, 4px));\n padding: var(--input-field-padding, 6px 9px);\n font: var(--input-field-font, inherit);\n }\n\n /*\n * 높이 — 한 줄짜리 칸은 높이 토큰 셋 중 하나다(page-standard.md §7). 기본은 보통(폼 · 팝업),\n * 표 안과 필터 막대는 small, 칸이 한두 개인 화면은 large. 단추와 같은 줄에 서면 같은 높이라\n * 윗선과 아랫선이 맞는다. textarea 는 여러 줄이라 높이를 정하지 않는다.\n */\n input:where(:not([type='checkbox'], [type='radio'], [type='range'], [type='color'], [type='file'])),\n select:where(:not([multiple], [size])) {\n height: var(--form-element-height-medium, 30px);\n }\n\n input:where([small]),\n select:where([small]) {\n height: var(--form-element-height-small, 24px);\n }\n\n input:where([large]),\n select:where([large]) {\n height: var(--form-element-height-large, 40px);\n }\n\n input:where(:not([type='checkbox'], [type='radio']))::placeholder,\n textarea::placeholder {\n color: var(--input-field-placeholder-color, var(--md-sys-color-on-surface-variant));\n /* 크기는 값과 같게 둔다 — 작게 만들면 같은 화면에서 필드마다 다른 인상을 준다 */\n opacity: 1;\n }\n\n input:where(:not([type='checkbox'], [type='radio']):focus-visible),\n select:where(:focus-visible),\n textarea:where(:focus-visible) {\n outline: none;\n border-color: var(--input-field-focus-color, var(--md-sys-color-primary));\n }\n\n input:where(:disabled),\n select:where(:disabled),\n textarea:where(:disabled) {\n opacity: var(--input-field-disabled-opacity, 0.5);\n cursor: default;\n }\n\n input:where(:read-only:not([type='checkbox'], [type='radio'])),\n textarea:where(:read-only) {\n background-color: var(--input-field-readonly-background, var(--md-sys-color-surface-container-low));\n }\n\n /* 체크박스·라디오는 모양을 건드리지 않고 색만 테마에 맞춘다 */\n input:where([type='checkbox'], [type='radio']) {\n accent-color: var(--input-field-accent-color, var(--md-sys-color-primary));\n }\n\n /*\n * :invalid 가 아니라 :user-invalid 를 쓴다. :invalid 는 required 빈 필드를 화면이 뜨는\n * 순간부터 붉게 칠하지만, :user-invalid 는 사용자가 입력하거나 제출을 시도한 뒤에만 반응한다.\n */\n input:where(:user-invalid),\n select:where(:user-invalid),\n textarea:where(:user-invalid) {\n border-color: var(--input-field-invalid-color, var(--md-sys-color-error));\n }\n`\n"]}
@@ -5,6 +5,7 @@ export * from './common-button-styles.js';
5
5
  export * from './form-field-styles.js';
6
6
  export * from './common-grist-styles.js';
7
7
  export * from './button-container-styles.js';
8
+ export * from './button-styles.js';
8
9
  export * from './common-header-styles.js';
9
10
  export * from './common-popup-styles.js';
10
11
  export * from './box-padding-editor-styles.js';
package/dist/src/index.js CHANGED
@@ -5,6 +5,7 @@ export * from './common-button-styles.js';
5
5
  export * from './form-field-styles.js';
6
6
  export * from './common-grist-styles.js';
7
7
  export * from './button-container-styles.js';
8
+ export * from './button-styles.js';
8
9
  export * from './common-header-styles.js';
9
10
  export * from './common-popup-styles.js';
10
11
  export * from './box-padding-editor-styles.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAA;AACpC,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AACzC,cAAc,wBAAwB,CAAA;AACtC,cAAc,0BAA0B,CAAA;AACxC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,2BAA2B,CAAA;AACzC,cAAc,0BAA0B,CAAA;AACxC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,2BAA2B,CAAA;AACzC,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AACzC,cAAc,wBAAwB,CAAA","sourcesContent":["export * from './headroom-styles.js'\nexport * from './scrollbar-styles.js'\nexport * from './spinner-styles.js'\nexport * from './common-button-styles.js'\nexport * from './form-field-styles.js'\nexport * from './common-grist-styles.js'\nexport * from './button-container-styles.js'\nexport * from './common-header-styles.js'\nexport * from './common-popup-styles.js'\nexport * from './box-padding-editor-styles.js'\nexport * from './gradient-direction-styles.js'\nexport * from './property-grid-styles.js'\nexport * from './table-event-styles.js'\nexport * from './inspector-styles.js'\nexport * from './line-styles.js'\nexport * from './action-heights.js'\nexport * from './form-element-heights.js'\nexport * from './page-frame-styles.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAA;AACpC,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AACzC,cAAc,wBAAwB,CAAA;AACtC,cAAc,0BAA0B,CAAA;AACxC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,oBAAoB,CAAA;AAClC,cAAc,2BAA2B,CAAA;AACzC,cAAc,0BAA0B,CAAA;AACxC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,2BAA2B,CAAA;AACzC,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AACzC,cAAc,wBAAwB,CAAA","sourcesContent":["export * from './headroom-styles.js'\nexport * from './scrollbar-styles.js'\nexport * from './spinner-styles.js'\nexport * from './common-button-styles.js'\nexport * from './form-field-styles.js'\nexport * from './common-grist-styles.js'\nexport * from './button-container-styles.js'\nexport * from './button-styles.js'\nexport * from './common-header-styles.js'\nexport * from './common-popup-styles.js'\nexport * from './box-padding-editor-styles.js'\nexport * from './gradient-direction-styles.js'\nexport * from './property-grid-styles.js'\nexport * from './table-event-styles.js'\nexport * from './inspector-styles.js'\nexport * from './line-styles.js'\nexport * from './action-heights.js'\nexport * from './form-element-heights.js'\nexport * from './page-frame-styles.js'\n"]}
@@ -1,32 +1,2 @@
1
- /**
2
- * Page frames (ADR-0060 decision 8). A screen picks the frame for what it is, and the frame owns the
3
- * spacing, width and scroll, so two screens of the same kind cannot come out different.
4
- *
5
- * The screen's name lives in the shell title bar (`context.title`, and `context.description` for one
6
- * line under it). A frame never draws the title again. Actions that change data go to the shell's
7
- * action bar (`context.actions`).
8
- *
9
- * ── Work surface ─────────────────────────────────────────────────────────
10
- * Lists, card catalogues, calendars: the content fills the page and scrolls inside itself.
11
- *
12
- * static styles = [WorkSurfacePageStyles]
13
- * <div class="page-headroom"> tabs · summary · filters </div>
14
- * <ox-grist class="page-fill"> … </ox-grist>
15
- *
16
- * ── Document ─────────────────────────────────────────────────────────────
17
- * Overviews, details, settings and forms: the page scrolls, one readable column, sections of cards.
18
- *
19
- * static styles = [DocumentPageStyles]
20
- * <div class="page">
21
- * <section class="page-section">
22
- * <header class="page-section-header"><h3>…</h3><div class="page-section-actions">…</div></header>
23
- * <p class="page-section-note">…</p>
24
- * <div class="page-card">…</div>
25
- * </section>
26
- * </div>
27
- *
28
- * Values come from the shell's --ui-* tokens, with fallbacks so a component shown without the shell
29
- * theme still has a shape.
30
- */
31
1
  export declare const WorkSurfacePageStyles: import("lit").CSSResult;
32
2
  export declare const DocumentPageStyles: import("lit").CSSResult;
@@ -28,8 +28,73 @@ import { css } from 'lit';
28
28
  *
29
29
  * Values come from the shell's --ui-* tokens, with fallbacks so a component shown without the shell
30
30
  * theme still has a shape.
31
+ *
32
+ * ── The ground a frame lays (2026-10-05) ────────────────────────────────
33
+ * A frame used to set only spacing and width. What a screen forgot fell to the browser's defaults:
34
+ * 16px body text (larger than a 15px section title), links in rgb(0 0 238) with a purple visited
35
+ * colour, Korean words broken mid-word, and cards 30px wider than their headers because padding was
36
+ * added to the width. Both frames now lay that ground, at zero specificity so a screen's own rule
37
+ * still wins (operato-application/design/ui/page-standard.md §5 · §6, ADR in operato-application/adr).
38
+ */
39
+ /**
40
+ * What both frames lay under a screen: body text, line height, colour, wrapping, box model, links.
41
+ *
42
+ * Links come in two kinds. A link to a thing (an identifier: a lot, an order, an activity) reads as
43
+ * text and underlines on hover. A link that takes the reader to another screen carries
44
+ * `class="page-nav-link"` and the accent colour. Neither keeps a visited colour.
31
45
  */
46
+ const PageGround = css `
47
+ :host {
48
+ color: var(--ui-ink, var(--md-sys-color-on-surface));
49
+ font-size: var(--ui-t-body, 13px);
50
+ line-height: 1.5;
51
+ /* Korean wraps at word boundaries; a word with nowhere to break (an id, a URL) still breaks. */
52
+ word-break: keep-all;
53
+ overflow-wrap: anywhere;
54
+ }
55
+
56
+ :where(*, *::before, *::after) {
57
+ box-sizing: border-box;
58
+ }
59
+
60
+ :where(h1, h2, h3, h4) {
61
+ text-wrap: balance;
62
+ }
63
+
64
+ :where(p) {
65
+ text-wrap: pretty;
66
+ }
67
+
68
+ :where(a) {
69
+ color: inherit;
70
+ text-decoration: none;
71
+ cursor: pointer;
72
+ }
73
+
74
+ :where(a.page-nav-link) {
75
+ color: var(--ui-accent, var(--md-sys-color-primary));
76
+ }
77
+
78
+ :where(a:hover, a:focus-visible) {
79
+ text-decoration: underline;
80
+ }
81
+
82
+ :where(.page-figure) {
83
+ font-variant-numeric: tabular-nums;
84
+ }
85
+
86
+ /*
87
+ * A run of prose (a paragraph, a section note) wraps at its box, and its box follows the section.
88
+ * Only on a very wide window does a line stop growing: one line no longer than about 100 Korean
89
+ * characters, so a 2,560px screen does not draw one sentence across the whole width.
90
+ */
91
+ :where(.page-prose, .page-section-note) {
92
+ max-width: var(--ui-prose-max, 100em);
93
+ }
94
+ `;
32
95
  export const WorkSurfacePageStyles = css `
96
+ ${PageGround}
97
+
33
98
  :host {
34
99
  display: flex;
35
100
  flex-direction: column;
@@ -45,7 +110,7 @@ export const WorkSurfacePageStyles = css `
45
110
  flex-wrap: wrap;
46
111
  align-items: center;
47
112
  gap: var(--spacing-medium, 8px);
48
- padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);
113
+ padding: var(--spacing-medium, 8px) var(--ui-gutter, 16px);
49
114
  }
50
115
 
51
116
  .page-headroom:empty {
@@ -59,6 +124,8 @@ export const WorkSurfacePageStyles = css `
59
124
  }
60
125
  `;
61
126
  export const DocumentPageStyles = css `
127
+ ${PageGround}
128
+
62
129
  :host {
63
130
  display: block;
64
131
  height: 100%;
@@ -68,16 +135,35 @@ export const DocumentPageStyles = css `
68
135
  }
69
136
 
70
137
  /*
71
- * One readable column, aligned with the title bar on the left. Wider screens do not stretch lines
72
- * past what the eye can follow back to the next line.
138
+ * The column fills the window between two equal gutters - the same gutter the title bar and the
139
+ * action bar use, so the title, every section and the buttons share one left and one right edge.
140
+ *
141
+ * It used to stop at 1080px and sit on the left, leaving the right side empty on a wide window
142
+ * (user decision 2026-10-05: fill the page, responsive). Long lines are kept in check by the
143
+ * section grid below and by the prose limit above, not by the page.
73
144
  */
74
145
  .page {
75
146
  box-sizing: border-box;
76
147
  display: flex;
77
148
  flex-direction: column;
78
- gap: 24px;
79
- max-width: var(--ui-measure, min(1080px, 100%));
80
- padding: 18px 20px 40px;
149
+ gap: var(--spacing-huge, 24px);
150
+ padding: 16px var(--ui-gutter, 16px) 40px;
151
+ }
152
+
153
+ /*
154
+ * Sections that can stand side by side: two columns from about 1,144px of column, three from about
155
+ * 1,728px, one on a narrow window. A section that needs the whole width (a table, a calendar, a
156
+ * gantt, or one that must be read in order) takes .page-section-wide.
157
+ */
158
+ .page-section-grid {
159
+ display: grid;
160
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 560px), 1fr));
161
+ gap: var(--spacing-huge, 24px);
162
+ align-items: start;
163
+ }
164
+
165
+ .page-section-grid > .page-section-wide {
166
+ grid-column: 1 / -1;
81
167
  }
82
168
 
83
169
  .page-section {
@@ -111,6 +197,7 @@ export const DocumentPageStyles = css `
111
197
 
112
198
  .page-section-note {
113
199
  margin: 0;
200
+ text-wrap: pretty;
114
201
  font-size: var(--ui-t-body, 13px);
115
202
  line-height: 1.5;
116
203
  color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
@@ -127,7 +214,18 @@ export const DocumentPageStyles = css `
127
214
  .page-card-grid {
128
215
  display: grid;
129
216
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
130
- gap: 12px;
217
+ gap: var(--spacing-large, 12px);
218
+ }
219
+
220
+ /* A narrow window: section actions drop under the title instead of squeezing it. */
221
+ @media (max-width: 600px) {
222
+ .page-section-header {
223
+ flex-wrap: wrap;
224
+ }
225
+
226
+ .page-section-actions {
227
+ margin-left: 0;
228
+ }
131
229
  }
132
230
  `;
133
231
  //# sourceMappingURL=page-frame-styles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"page-frame-styles.js","sourceRoot":"","sources":["../../src/page-frame-styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BvC,CAAA;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuEpC,CAAA","sourcesContent":["import { css } from 'lit'\n\n/**\n * Page frames (ADR-0060 decision 8). A screen picks the frame for what it is, and the frame owns the\n * spacing, width and scroll, so two screens of the same kind cannot come out different.\n *\n * The screen's name lives in the shell title bar (`context.title`, and `context.description` for one\n * line under it). A frame never draws the title again. Actions that change data go to the shell's\n * action bar (`context.actions`).\n *\n * ── Work surface ─────────────────────────────────────────────────────────\n * Lists, card catalogues, calendars: the content fills the page and scrolls inside itself.\n *\n * static styles = [WorkSurfacePageStyles]\n * <div class=\"page-headroom\"> tabs · summary · filters </div>\n * <ox-grist class=\"page-fill\"> … </ox-grist>\n *\n * ── Document ─────────────────────────────────────────────────────────────\n * Overviews, details, settings and forms: the page scrolls, one readable column, sections of cards.\n *\n * static styles = [DocumentPageStyles]\n * <div class=\"page\">\n * <section class=\"page-section\">\n * <header class=\"page-section-header\"><h3>…</h3><div class=\"page-section-actions\">…</div></header>\n * <p class=\"page-section-note\">…</p>\n * <div class=\"page-card\">…</div>\n * </section>\n * </div>\n *\n * Values come from the shell's --ui-* tokens, with fallbacks so a component shown without the shell\n * theme still has a shape.\n */\n\nexport const WorkSurfacePageStyles = css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n box-sizing: border-box;\n background-color: var(--ui-bg, var(--md-sys-color-surface));\n }\n\n /* What narrows or frames the content: tabs, a summary, filters. Sits right above it. */\n .page-headroom {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n }\n\n .page-headroom:empty {\n display: none;\n }\n\n /* The content: fills what is left and scrolls inside itself. */\n .page-fill {\n flex: 1;\n min-height: 0;\n }\n`\n\nexport const DocumentPageStyles = css`\n :host {\n display: block;\n height: 100%;\n overflow: auto;\n box-sizing: border-box;\n background-color: var(--ui-bg, var(--md-sys-color-surface));\n }\n\n /*\n * One readable column, aligned with the title bar on the left. Wider screens do not stretch lines\n * past what the eye can follow back to the next line.\n */\n .page {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 24px;\n max-width: var(--ui-measure, min(1080px, 100%));\n padding: 18px 20px 40px;\n }\n\n .page-section {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium, 8px);\n }\n\n .page-section-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n min-height: 32px;\n }\n\n .page-section-header h3 {\n margin: 0;\n font-size: var(--ui-t-title, 15px);\n font-weight: 600;\n line-height: 1.35;\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n }\n\n /* Actions that belong to this section only; page-wide ones go to the shell action bar. */\n .page-section-actions {\n display: flex;\n align-items: center;\n gap: var(--spacing-small, 4px);\n margin-left: auto;\n }\n\n .page-section-note {\n margin: 0;\n font-size: var(--ui-t-body, 13px);\n line-height: 1.5;\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n\n .page-card {\n box-sizing: border-box;\n padding: 16px;\n border: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n border-radius: var(--ui-r, 10px);\n background-color: var(--ui-panel, var(--md-sys-color-surface-container-lowest));\n }\n\n .page-card-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n gap: 12px;\n }\n`\n"]}
1
+ {"version":3,"file":"page-frame-styles.js","sourceRoot":"","sources":["../../src/page-frame-styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDrB,CAAA;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAA;IACpC,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6Bb,CAAA;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAA;IACjC,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuGb,CAAA","sourcesContent":["import { css } from 'lit'\n\n/**\n * Page frames (ADR-0060 decision 8). A screen picks the frame for what it is, and the frame owns the\n * spacing, width and scroll, so two screens of the same kind cannot come out different.\n *\n * The screen's name lives in the shell title bar (`context.title`, and `context.description` for one\n * line under it). A frame never draws the title again. Actions that change data go to the shell's\n * action bar (`context.actions`).\n *\n * ── Work surface ─────────────────────────────────────────────────────────\n * Lists, card catalogues, calendars: the content fills the page and scrolls inside itself.\n *\n * static styles = [WorkSurfacePageStyles]\n * <div class=\"page-headroom\"> tabs · summary · filters </div>\n * <ox-grist class=\"page-fill\"> … </ox-grist>\n *\n * ── Document ─────────────────────────────────────────────────────────────\n * Overviews, details, settings and forms: the page scrolls, one readable column, sections of cards.\n *\n * static styles = [DocumentPageStyles]\n * <div class=\"page\">\n * <section class=\"page-section\">\n * <header class=\"page-section-header\"><h3>…</h3><div class=\"page-section-actions\">…</div></header>\n * <p class=\"page-section-note\">…</p>\n * <div class=\"page-card\">…</div>\n * </section>\n * </div>\n *\n * Values come from the shell's --ui-* tokens, with fallbacks so a component shown without the shell\n * theme still has a shape.\n *\n * ── The ground a frame lays (2026-10-05) ────────────────────────────────\n * A frame used to set only spacing and width. What a screen forgot fell to the browser's defaults:\n * 16px body text (larger than a 15px section title), links in rgb(0 0 238) with a purple visited\n * colour, Korean words broken mid-word, and cards 30px wider than their headers because padding was\n * added to the width. Both frames now lay that ground, at zero specificity so a screen's own rule\n * still wins (operato-application/design/ui/page-standard.md §5 · §6, ADR in operato-application/adr).\n */\n\n/**\n * What both frames lay under a screen: body text, line height, colour, wrapping, box model, links.\n *\n * Links come in two kinds. A link to a thing (an identifier: a lot, an order, an activity) reads as\n * text and underlines on hover. A link that takes the reader to another screen carries\n * `class=\"page-nav-link\"` and the accent colour. Neither keeps a visited colour.\n */\nconst PageGround = css`\n :host {\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n font-size: var(--ui-t-body, 13px);\n line-height: 1.5;\n /* Korean wraps at word boundaries; a word with nowhere to break (an id, a URL) still breaks. */\n word-break: keep-all;\n overflow-wrap: anywhere;\n }\n\n :where(*, *::before, *::after) {\n box-sizing: border-box;\n }\n\n :where(h1, h2, h3, h4) {\n text-wrap: balance;\n }\n\n :where(p) {\n text-wrap: pretty;\n }\n\n :where(a) {\n color: inherit;\n text-decoration: none;\n cursor: pointer;\n }\n\n :where(a.page-nav-link) {\n color: var(--ui-accent, var(--md-sys-color-primary));\n }\n\n :where(a:hover, a:focus-visible) {\n text-decoration: underline;\n }\n\n :where(.page-figure) {\n font-variant-numeric: tabular-nums;\n }\n\n /*\n * A run of prose (a paragraph, a section note) wraps at its box, and its box follows the section.\n * Only on a very wide window does a line stop growing: one line no longer than about 100 Korean\n * characters, so a 2,560px screen does not draw one sentence across the whole width.\n */\n :where(.page-prose, .page-section-note) {\n max-width: var(--ui-prose-max, 100em);\n }\n`\n\nexport const WorkSurfacePageStyles = css`\n ${PageGround}\n\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n box-sizing: border-box;\n background-color: var(--ui-bg, var(--md-sys-color-surface));\n }\n\n /* What narrows or frames the content: tabs, a summary, filters. Sits right above it. */\n .page-headroom {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-medium, 8px) var(--ui-gutter, 16px);\n }\n\n .page-headroom:empty {\n display: none;\n }\n\n /* The content: fills what is left and scrolls inside itself. */\n .page-fill {\n flex: 1;\n min-height: 0;\n }\n`\n\nexport const DocumentPageStyles = css`\n ${PageGround}\n\n :host {\n display: block;\n height: 100%;\n overflow: auto;\n box-sizing: border-box;\n background-color: var(--ui-bg, var(--md-sys-color-surface));\n }\n\n /*\n * The column fills the window between two equal gutters - the same gutter the title bar and the\n * action bar use, so the title, every section and the buttons share one left and one right edge.\n *\n * It used to stop at 1080px and sit on the left, leaving the right side empty on a wide window\n * (user decision 2026-10-05: fill the page, responsive). Long lines are kept in check by the\n * section grid below and by the prose limit above, not by the page.\n */\n .page {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: var(--spacing-huge, 24px);\n padding: 16px var(--ui-gutter, 16px) 40px;\n }\n\n /*\n * Sections that can stand side by side: two columns from about 1,144px of column, three from about\n * 1,728px, one on a narrow window. A section that needs the whole width (a table, a calendar, a\n * gantt, or one that must be read in order) takes .page-section-wide.\n */\n .page-section-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 560px), 1fr));\n gap: var(--spacing-huge, 24px);\n align-items: start;\n }\n\n .page-section-grid > .page-section-wide {\n grid-column: 1 / -1;\n }\n\n .page-section {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium, 8px);\n }\n\n .page-section-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n min-height: 32px;\n }\n\n .page-section-header h3 {\n margin: 0;\n font-size: var(--ui-t-title, 15px);\n font-weight: 600;\n line-height: 1.35;\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n }\n\n /* Actions that belong to this section only; page-wide ones go to the shell action bar. */\n .page-section-actions {\n display: flex;\n align-items: center;\n gap: var(--spacing-small, 4px);\n margin-left: auto;\n }\n\n .page-section-note {\n margin: 0;\n text-wrap: pretty;\n font-size: var(--ui-t-body, 13px);\n line-height: 1.5;\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n\n .page-card {\n box-sizing: border-box;\n padding: 16px;\n border: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n border-radius: var(--ui-r, 10px);\n background-color: var(--ui-panel, var(--md-sys-color-surface-container-lowest));\n }\n\n .page-card-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n gap: var(--spacing-large, 12px);\n }\n\n /* A narrow window: section actions drop under the title instead of squeezing it. */\n @media (max-width: 600px) {\n .page-section-header {\n flex-wrap: wrap;\n }\n\n .page-section-actions {\n margin-left: 0;\n }\n }\n`\n"]}
@@ -0,0 +1 @@
1
+ export {};