@gtivr4/a1-design-system-react 0.1.0 → 0.2.4

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.
Files changed (111) hide show
  1. package/guidelines/Guidelines.md +228 -0
  2. package/package.json +4 -1
  3. package/src/breakpoints.css +29 -0
  4. package/src/color-scheme.css +586 -24
  5. package/src/components/accordion/Accordion.jsx +80 -0
  6. package/src/components/accordion/accordion.css +118 -0
  7. package/src/components/banner/Banner.jsx +66 -0
  8. package/src/components/banner/banner.css +205 -0
  9. package/src/components/bleed/Bleed.jsx +27 -0
  10. package/src/components/bleed/bleed.css +5 -0
  11. package/src/components/blockquote/Blockquote.jsx +40 -0
  12. package/src/components/blockquote/blockquote.css +166 -0
  13. package/src/components/breadcrumb/Breadcrumb.jsx +82 -0
  14. package/src/components/breadcrumb/breadcrumb.css +133 -0
  15. package/src/components/button/button.css +42 -12
  16. package/src/components/button-container/ButtonContainer.jsx +20 -1
  17. package/src/components/button-container/button-container.css +19 -1
  18. package/src/components/calendar/Calendar.jsx +383 -0
  19. package/src/components/calendar/calendar.css +225 -0
  20. package/src/components/card/Card.jsx +50 -12
  21. package/src/components/card/card.css +178 -14
  22. package/src/components/checkbox-group/CheckboxGroup.jsx +120 -0
  23. package/src/components/checkbox-group/checkbox-group.css +304 -0
  24. package/src/components/cluster/Cluster.jsx +52 -0
  25. package/src/components/cluster/cluster.css +9 -0
  26. package/src/components/code/Code.jsx +135 -0
  27. package/src/components/code/code.css +60 -0
  28. package/src/components/data-table/DataTable.jsx +721 -0
  29. package/src/components/data-table/DataTableFilters.jsx +339 -0
  30. package/src/components/data-table/data-table-filters.css +259 -0
  31. package/src/components/data-table/data-table.css +425 -0
  32. package/src/components/dialog/Dialog.jsx +45 -2
  33. package/src/components/dialog/dialog.css +13 -4
  34. package/src/components/divider/Divider.jsx +64 -0
  35. package/src/components/divider/divider.css +170 -0
  36. package/src/components/field/CreditCardField.jsx +131 -0
  37. package/src/components/field/DateField.jsx +11 -0
  38. package/src/components/field/NumberField.jsx +11 -0
  39. package/src/components/field/PhoneField.jsx +107 -0
  40. package/src/components/field/SelectField.jsx +86 -0
  41. package/src/components/field/TextField.jsx +83 -0
  42. package/src/components/field/TextareaField.jsx +147 -0
  43. package/src/components/field/TimeField.jsx +11 -0
  44. package/src/components/field/ZipField.jsx +114 -0
  45. package/src/components/field/credit-card.css +30 -0
  46. package/src/components/field/field.css +380 -0
  47. package/src/components/field/textarea-field.css +185 -0
  48. package/src/components/field-row/FieldRow.jsx +23 -0
  49. package/src/components/field-row/field-row.css +51 -0
  50. package/src/components/fieldset/Fieldset.jsx +49 -0
  51. package/src/components/fieldset/fieldset.css +75 -0
  52. package/src/components/figure/Figure.jsx +63 -0
  53. package/src/components/figure/figure.css +97 -0
  54. package/src/components/grid/Grid.jsx +36 -2
  55. package/src/components/grid/grid.css +129 -4
  56. package/src/components/heading/Heading.jsx +41 -1
  57. package/src/components/heading/heading.css +65 -4
  58. package/src/components/icon/icon.css +1 -0
  59. package/src/components/icon-button/icon-button.css +1 -0
  60. package/src/components/inline/inline.css +51 -0
  61. package/src/components/inline-editable/InlineEditable.jsx +77 -0
  62. package/src/components/inline-editable/inline-editable.css +47 -0
  63. package/src/components/inset/Inset.jsx +27 -0
  64. package/src/components/inset/inset.css +6 -0
  65. package/src/components/labels/Labels.jsx +5 -5
  66. package/src/components/link/Link.jsx +2 -3
  67. package/src/components/link/link.css +30 -1
  68. package/src/components/list/List.jsx +92 -0
  69. package/src/components/list/list.css +178 -0
  70. package/src/components/menu/Menu.jsx +243 -10
  71. package/src/components/menu/menu.css +157 -17
  72. package/src/components/message/Message.jsx +25 -50
  73. package/src/components/message/message.css +50 -33
  74. package/src/components/notification/Notification.jsx +1 -1
  75. package/src/components/page-layout/PageLayout.jsx +16 -1
  76. package/src/components/page-layout/page-layout.css +97 -4
  77. package/src/components/page-nav/PageNav.jsx +110 -0
  78. package/src/components/page-nav/page-nav.css +167 -0
  79. package/src/components/paragraph/Paragraph.jsx +35 -2
  80. package/src/components/paragraph/paragraph.css +38 -1
  81. package/src/components/radio-group/RadioGroup.jsx +121 -0
  82. package/src/components/radio-group/radio-group.css +268 -0
  83. package/src/components/section/Section.jsx +108 -0
  84. package/src/components/section/section.css +280 -0
  85. package/src/components/segmented-control/SegmentedControl.jsx +4 -0
  86. package/src/components/segmented-control/segmented.css +13 -0
  87. package/src/components/side-nav/SideNav.jsx +29 -9
  88. package/src/components/side-nav/scrim.css +1 -1
  89. package/src/components/side-nav/side-nav.css +70 -32
  90. package/src/components/snackbar/Snackbar.jsx +56 -0
  91. package/src/components/snackbar/snackbar.css +113 -0
  92. package/src/components/spacer/Spacer.jsx +36 -0
  93. package/src/components/spacer/spacer.css +44 -0
  94. package/src/components/stack/Stack.jsx +100 -0
  95. package/src/components/stack/stack.css +37 -0
  96. package/src/components/switch/Switch.jsx +114 -0
  97. package/src/components/switch/switch.css +276 -0
  98. package/src/components/system-banner/SystemBanner.jsx +57 -0
  99. package/src/components/system-banner/system-banner.css +118 -0
  100. package/src/components/tabs/Tabs.jsx +96 -28
  101. package/src/components/tabs/tabs.css +352 -15
  102. package/src/components/token-select/TokenSelect.jsx +159 -0
  103. package/src/components/token-select/token-select.css +110 -0
  104. package/src/components/top-header/TopHeader.jsx +641 -0
  105. package/src/components/top-header/top-header.css +337 -0
  106. package/src/illustrations/ComponentThumbnails.jsx +227 -0
  107. package/src/index.js +41 -5
  108. package/src/themes.css +256 -5
  109. package/src/tokens.css +919 -0
  110. package/src/utilities/spacing.css +8 -0
  111. package/src/utilities/sr-only.css +16 -0
@@ -1,45 +1,185 @@
1
+ /* ── Menu shell ───────────────────────────────────────────────────────────── */
2
+
1
3
  .a1-menu {
2
- /* Override browser centering; place below the header on the right */
3
4
  position: fixed;
4
- top: 72px; /* 64px header + 8px gap */
5
- right: var(--base-spacing-16);
5
+ z-index: var(--component-menu-z-index, 1000);
6
+ top: var(--a1-menu-top, 0);
7
+ right: auto;
6
8
  bottom: auto;
7
- left: auto;
9
+ left: var(--a1-menu-left, 0);
8
10
  margin: 0;
11
+ box-sizing: border-box;
9
12
 
10
- width: 260px;
11
- max-height: calc(100dvh - 88px);
13
+ width: var(--component-menu-width);
14
+ max-width: calc(100dvw - (var(--component-menu-viewport-offset) * 2));
15
+ max-height: var(--a1-menu-max-height, calc(100dvh - var(--component-menu-viewport-offset)));
16
+ overflow-x: hidden;
12
17
  overflow-y: auto;
18
+ overscroll-behavior: contain;
13
19
 
14
- padding: var(--base-spacing-20);
15
- border: 1px solid var(--semantic-color-border-subtle);
20
+ padding: var(--base-spacing-8);
21
+ border: var(--component-menu-border-width) solid var(--semantic-color-border-subtle);
16
22
  border-radius: var(--base-radius-lg);
17
23
  background: var(--semantic-color-surface-panel);
18
24
  box-shadow: var(--semantic-shadow-lg);
19
25
  color: var(--semantic-color-text-default);
20
26
  }
21
27
 
28
+ .a1-menu > .a1-menu__close {
29
+ display: none;
30
+ }
31
+
22
32
  .a1-menu::backdrop {
23
- background: color-mix(in srgb, var(--base-color-neutral-900) 30%, transparent);
33
+ background: transparent;
24
34
  }
25
35
 
36
+ /* ── Section ─────────────────────────────────────────────────────────────── */
37
+
26
38
  .a1-menu__section {
27
39
  display: flex;
28
40
  flex-direction: column;
29
- gap: var(--base-spacing-8);
41
+ gap: var(--base-spacing-2);
42
+ min-width: 0;
30
43
  }
31
44
 
32
- .a1-menu__section + .a1-menu__section {
33
- margin-top: var(--base-spacing-16);
34
- padding-top: var(--base-spacing-16);
35
- border-top: 1px solid var(--semantic-color-border-subtle);
45
+ .a1-menu__section:first-of-type .a1-menu__section-divider {
46
+ display: none;
36
47
  }
37
48
 
38
49
  .a1-menu__section-label {
39
50
  margin: 0;
51
+ padding: var(--base-spacing-4) var(--base-spacing-8);
40
52
  color: var(--semantic-color-text-muted);
41
53
  font-size: var(--semantic-font-size-body-xs);
42
- font-weight: 700;
43
- text-transform: uppercase;
44
- letter-spacing: 0.06em;
54
+ font-weight: var(--component-menu-section-label-font-weight);
55
+ overflow-wrap: anywhere;
56
+ }
57
+
58
+ /* ── MenuItem ────────────────────────────────────────────────────────────── */
59
+
60
+ .a1-menu-item {
61
+ display: flex;
62
+ align-items: center;
63
+ gap: var(--base-spacing-8);
64
+ box-sizing: border-box;
65
+ width: 100%;
66
+ min-width: 0;
67
+ max-width: 100%;
68
+ padding: var(--base-spacing-6) var(--base-spacing-8);
69
+ border: none;
70
+ border-radius: var(--base-radius-control);
71
+ background: transparent;
72
+ color: var(--semantic-color-text-default);
73
+ font-family: var(--semantic-font-family-body);
74
+ font-size: var(--semantic-font-size-body-sm);
75
+ font-weight: var(--component-menu-item-font-weight);
76
+ line-height: var(--component-menu-item-line-height);
77
+ text-align: start;
78
+ text-decoration: none;
79
+ cursor: pointer;
80
+ transition: background var(--semantic-motion-duration-fast) var(--semantic-motion-easing-standard);
81
+ }
82
+
83
+ .a1-menu-item:hover {
84
+ background: color-mix(in srgb, var(--semantic-color-surface-panel), var(--semantic-color-text-default) 6%);
85
+ }
86
+
87
+ .a1-menu-item:active {
88
+ background: color-mix(in srgb, var(--semantic-color-surface-panel), var(--semantic-color-text-default) 12%);
89
+ }
90
+
91
+ .a1-menu-item:focus-visible {
92
+ outline: var(--component-menu-item-focus-ring-width) solid var(--semantic-color-text-accent);
93
+ outline-offset: var(--component-menu-item-focus-ring-offset);
94
+ }
95
+
96
+ .a1-menu-item:disabled,
97
+ .a1-menu-item[aria-disabled="true"] {
98
+ opacity: var(--component-menu-item-disabled-opacity);
99
+ cursor: not-allowed;
100
+ }
101
+
102
+ .a1-menu-item:disabled:hover,
103
+ .a1-menu-item[aria-disabled="true"]:hover {
104
+ background: transparent;
105
+ }
106
+
107
+ /* Active / current-page indicator */
108
+ .a1-menu-item--active {
109
+ color: var(--semantic-color-text-default);
110
+ background: var(--semantic-color-action-surface);
111
+ border-inline-start: 2px solid var(--semantic-color-action-background);
112
+ padding-inline-start: calc(var(--base-spacing-8) - 2px);
113
+ }
114
+
115
+ .a1-menu-item--active .a1-menu-item__icon {
116
+ color: var(--semantic-color-action-background);
117
+ }
118
+
119
+ /* Destructive variant */
120
+ .a1-menu-item--destructive {
121
+ color: var(--semantic-color-status-error-text);
122
+ }
123
+
124
+ .a1-menu-item--destructive:hover {
125
+ background: var(--semantic-color-status-error-surface);
126
+ }
127
+
128
+ .a1-menu-item--destructive:active {
129
+ background: color-mix(in srgb, var(--semantic-color-status-error-surface), var(--semantic-color-status-error-text) 12%);
130
+ }
131
+
132
+ /* Parts */
133
+ .a1-menu-item__icon {
134
+ font-size: var(--component-menu-item-icon-size);
135
+ flex-shrink: 0;
136
+ color: var(--semantic-color-text-muted);
137
+ }
138
+
139
+ .a1-menu-item--destructive .a1-menu-item__icon {
140
+ color: var(--semantic-color-status-error-text);
141
+ }
142
+
143
+ .a1-menu-item__label {
144
+ flex: 1 1 auto;
145
+ min-width: 0;
146
+ overflow-wrap: anywhere;
147
+ }
148
+
149
+ .a1-menu-item__shortcut {
150
+ flex: 0 1 auto;
151
+ max-width: 45%;
152
+ overflow: hidden;
153
+ font-size: var(--semantic-font-size-body-xs);
154
+ color: var(--semantic-color-text-muted);
155
+ text-overflow: ellipsis;
156
+ white-space: nowrap;
157
+ }
158
+
159
+ @media (--bp-xs) {
160
+ .a1-menu {
161
+ inset: auto 0 0;
162
+ width: 100dvw;
163
+ max-width: 100dvw;
164
+ max-height: min(var(--component-menu-mobile-max-height), calc(100dvh - var(--base-spacing-32)));
165
+ padding:
166
+ var(--base-spacing-16)
167
+ var(--base-spacing-16)
168
+ calc(var(--base-spacing-40) + env(safe-area-inset-bottom, 0px));
169
+ border-right: none;
170
+ border-bottom: none;
171
+ border-left: none;
172
+ border-radius: var(--base-radius-xl) var(--base-radius-xl) 0 0;
173
+ }
174
+
175
+ .a1-menu > .a1-menu__close {
176
+ display: flex;
177
+ margin-inline-start: auto;
178
+ margin-bottom: var(--base-spacing-8);
179
+ }
180
+
181
+ .a1-menu::backdrop {
182
+ background: var(--component-scrim-color);
183
+ backdrop-filter: blur(var(--component-scrim-blur));
184
+ }
45
185
  }
@@ -1,6 +1,7 @@
1
1
  import "./message.css";
2
2
  import { Icon } from "../icon/Icon.jsx";
3
- import { IconButton } from "../icon-button/IconButton.jsx";
3
+ import { Heading } from "../heading/Heading.jsx";
4
+ import { Paragraph } from "../paragraph/Paragraph.jsx";
4
5
 
5
6
  const STATUS_ICONS = {
6
7
  neutral: "info",
@@ -13,65 +14,30 @@ const STATUS_ICONS = {
13
14
  const STATUSES = ["neutral", "info", "success", "warn", "error"];
14
15
  const ES_SCALES = ["page", "section", "card"];
15
16
 
16
- /* ═══════════════════════════════════════════════════════════════════════════
17
- MessageBanner
18
- ═══════════════════════════════════════════════════════════════════════════ */
19
-
20
- export function MessageBanner({
21
- status = "neutral",
22
- title,
23
- icon,
24
- onDismiss,
25
- children,
26
- }) {
27
- const resolvedStatus = STATUSES.includes(status) ? status : "neutral";
28
- const resolvedIcon = icon ?? STATUS_ICONS[resolvedStatus];
29
-
30
- return (
31
- <div
32
- className={`a1-message-banner a1-message-banner--${resolvedStatus}`}
33
- role="alert"
34
- aria-live="polite"
35
- >
36
- <span className="a1-message-banner__icon" aria-hidden="true">
37
- <Icon name={resolvedIcon} />
38
- </span>
39
-
40
- <div className="a1-message-banner__content">
41
- {title && <p className="a1-message-banner__title">{title}</p>}
42
- {children && <p className="a1-message-banner__body">{children}</p>}
43
- </div>
44
-
45
- {onDismiss && (
46
- <IconButton
47
- icon="close"
48
- label="Dismiss"
49
- onClick={onDismiss}
50
- className="a1-message-banner__dismiss"
51
- />
52
- )}
53
- </div>
54
- );
55
- }
17
+ const ES_SCALE_CONFIG = {
18
+ page: { headingAs: "h1", headingSize: "sm", paragraphSize: "lg" },
19
+ section: { headingAs: "h2", headingSize: "xs", paragraphSize: "md" },
20
+ card: { headingAs: "h3", headingSize: "xs", paragraphSize: "sm" },
21
+ };
56
22
 
57
23
  /* ═══════════════════════════════════════════════════════════════════════════
58
24
  MessageBadge (inline filled status chip)
59
25
  ═══════════════════════════════════════════════════════════════════════════ */
60
26
 
61
- const VARIANTS = ["bold", "subtle"];
62
-
63
- export function MessageBadge({ status = "neutral", variant = "bold", icon, children }) {
27
+ export function MessageBadge({ status = "neutral", subtle = false, size = "md", icon, children }) {
64
28
  const resolvedStatus = STATUSES.includes(status) ? status : "neutral";
65
- const resolvedVariant = VARIANTS.includes(variant) ? variant : "bold";
66
- const resolvedIcon = icon ?? STATUS_ICONS[resolvedStatus];
29
+ // icon={null} explicitly suppresses the icon; undefined falls back to the status default
30
+ const resolvedIcon = icon === null ? null : (icon ?? STATUS_ICONS[resolvedStatus]);
67
31
 
68
32
  return (
69
33
  <span className={[
70
34
  "a1-message-badge",
71
35
  `a1-message-badge--${resolvedStatus}`,
72
- resolvedVariant === "subtle" && "a1-message-badge--subtle",
36
+ subtle && "a1-message-badge--subtle",
37
+ size === "sm" && "a1-message-badge--sm",
38
+ size === "lg" && "a1-message-badge--lg",
73
39
  ].filter(Boolean).join(" ")}>
74
- <Icon name={resolvedIcon} />
40
+ {resolvedIcon && <Icon name={resolvedIcon} />}
75
41
  {children}
76
42
  </span>
77
43
  );
@@ -89,14 +55,23 @@ export function MessageEmptyState({
89
55
  action,
90
56
  }) {
91
57
  const resolvedScale = ES_SCALES.includes(scale) ? scale : "section";
58
+ const { headingAs, headingSize, paragraphSize } = ES_SCALE_CONFIG[resolvedScale];
92
59
 
93
60
  return (
94
61
  <div className={`a1-message-empty a1-message-empty--${resolvedScale}`}>
95
62
  <div className="a1-message-empty__icon-wrap" aria-hidden="true">
96
63
  <Icon name={icon} />
97
64
  </div>
98
- {title && <p className="a1-message-empty__title">{title}</p>}
99
- {description && <p className="a1-message-empty__description">{description}</p>}
65
+ {title && (
66
+ <Heading as={headingAs} size={headingSize} className="a1-message-empty__title">
67
+ {title}
68
+ </Heading>
69
+ )}
70
+ {description && (
71
+ <Paragraph size={paragraphSize} color="muted" className="a1-message-empty__description">
72
+ {description}
73
+ </Paragraph>
74
+ )}
100
75
  {action && <div className="a1-message-empty__action">{action}</div>}
101
76
  </div>
102
77
  );
@@ -3,6 +3,7 @@
3
3
  ═══════════════════════════════════════════════════════════════════════════ */
4
4
 
5
5
  .a1-message-banner {
6
+ box-sizing: border-box;
6
7
  /* defaults: neutral */
7
8
  --a1-msg-surface: var(--semantic-color-surface-panel);
8
9
  --a1-msg-border: var(--semantic-color-border-subtle);
@@ -42,7 +43,7 @@
42
43
  flex-shrink: 0;
43
44
  color: var(--a1-msg-accent);
44
45
  font-size: var(--component-message-banner-icon-size);
45
- line-height: 1;
46
+ line-height: var(--component-message-badge-line-height);
46
47
  margin-top: var(--component-message-banner-icon-margin-top);
47
48
  --a1-icon-opsz: var(--component-message-banner-icon-optical-size);
48
49
  }
@@ -80,22 +81,27 @@
80
81
  }
81
82
 
82
83
  /* ═══════════════════════════════════════════════════════════════════════════
83
- MessageBadge (inline status chip — filled)
84
+ MessageBadge (inline status chip)
84
85
  ═══════════════════════════════════════════════════════════════════════════ */
85
86
 
86
87
  .a1-message-badge {
87
- /* defaults: neutral */
88
- --a1-msg-accent: var(--semantic-color-action-background);
89
- --a1-msg-foreground: var(--semantic-color-action-foreground);
88
+ /* neutral defaults bold and subtle vars */
89
+ --a1-badge-bg: var(--semantic-color-text-muted);
90
+ --a1-badge-fg: var(--semantic-color-text-inverse);
91
+ --a1-badge-subtle-bg: var(--semantic-color-surface-raised);
92
+ --a1-badge-subtle-fg: var(--semantic-color-text-muted);
93
+ --a1-badge-subtle-border: var(--semantic-color-border-default);
90
94
 
91
95
  display: inline-flex;
96
+ width: fit-content;
97
+ max-width: 100%;
92
98
  align-items: center;
93
99
  gap: var(--base-spacing-4);
94
100
  padding: var(--component-message-badge-padding-block)
95
101
  var(--component-message-badge-padding-inline);
96
102
  border-radius: var(--component-message-badge-border-radius);
97
- background: var(--a1-msg-accent);
98
- color: var(--a1-msg-foreground);
103
+ background: var(--a1-badge-bg);
104
+ color: var(--a1-badge-fg);
99
105
  font-family: var(--component-paragraph-font-family);
100
106
  font-size: var(--semantic-font-size-body-sm);
101
107
  font-weight: var(--component-message-badge-font-weight);
@@ -104,31 +110,55 @@
104
110
  }
105
111
 
106
112
  .a1-message-badge--info {
107
- --a1-msg-accent: var(--semantic-color-status-info-background);
108
- --a1-msg-foreground: var(--semantic-color-status-info-foreground);
113
+ --a1-badge-bg: var(--semantic-color-status-info-background);
114
+ --a1-badge-fg: var(--semantic-color-status-info-foreground);
115
+ --a1-badge-subtle-bg: var(--semantic-color-status-info-surface);
116
+ --a1-badge-subtle-fg: var(--semantic-color-status-info-background);
117
+ --a1-badge-subtle-border: var(--semantic-color-status-info-border);
109
118
  }
110
119
  .a1-message-badge--success {
111
- --a1-msg-accent: var(--semantic-color-status-success-background);
112
- --a1-msg-foreground: var(--semantic-color-status-success-foreground);
120
+ --a1-badge-bg: var(--semantic-color-status-success-background);
121
+ --a1-badge-fg: var(--semantic-color-status-success-foreground);
122
+ --a1-badge-subtle-bg: var(--semantic-color-status-success-surface);
123
+ --a1-badge-subtle-fg: var(--semantic-color-status-success-background);
124
+ --a1-badge-subtle-border: var(--semantic-color-status-success-border);
113
125
  }
114
126
  .a1-message-badge--warn {
115
- --a1-msg-accent: var(--semantic-color-status-warn-background);
116
- --a1-msg-foreground: var(--semantic-color-status-warn-foreground);
127
+ --a1-badge-bg: var(--base-color-warn-200);
128
+ --a1-badge-fg: var(--base-color-neutral-900);
129
+ --a1-badge-subtle-bg: var(--semantic-color-status-warn-surface);
130
+ --a1-badge-subtle-fg: var(--semantic-color-status-warn-text);
131
+ --a1-badge-subtle-border: var(--semantic-color-status-warn-border);
117
132
  }
118
133
  .a1-message-badge--error {
119
- --a1-msg-accent: var(--semantic-color-status-error-background);
120
- --a1-msg-foreground: var(--semantic-color-status-error-foreground);
134
+ --a1-badge-bg: var(--semantic-color-status-error-background);
135
+ --a1-badge-fg: var(--semantic-color-status-error-foreground);
136
+ --a1-badge-subtle-bg: var(--semantic-color-status-error-surface);
137
+ --a1-badge-subtle-fg: var(--semantic-color-status-error-text);
138
+ --a1-badge-subtle-border: var(--semantic-color-status-error-border);
121
139
  }
122
140
 
123
- /* Subtle variant light surface + border, no fill */
141
+ /* subtleoverrides to the surface/outlined style for whichever status is active */
124
142
  .a1-message-badge--subtle {
125
- --a1-msg-accent: var(--semantic-color-action-surface);
126
- --a1-msg-foreground: var(--semantic-color-action-background);
127
- border: 1px solid var(--semantic-color-action-border);
143
+ --a1-badge-bg: var(--a1-badge-subtle-bg);
144
+ --a1-badge-fg: var(--a1-badge-subtle-fg);
145
+ outline: var(--component-message-badge-border-width) solid var(--a1-badge-subtle-border);
146
+ }
147
+
148
+ .a1-message-badge--sm {
149
+ padding: var(--component-message-badge-sm-padding-block)
150
+ var(--component-message-badge-sm-padding-inline);
151
+ font-size: var(--semantic-font-size-body-xs);
152
+ }
153
+
154
+ .a1-message-badge--lg {
155
+ padding: var(--component-message-badge-lg-padding-block)
156
+ var(--component-message-badge-lg-padding-inline);
157
+ font-size: var(--semantic-font-size-body-md);
128
158
  }
129
159
 
130
160
  .a1-message-badge .a1-icon {
131
- font-size: 1em;
161
+ font-size: var(--component-message-badge-icon-size);
132
162
  }
133
163
 
134
164
  /* ═══════════════════════════════════════════════════════════════════════════
@@ -202,25 +232,12 @@
202
232
 
203
233
  .a1-message-empty__title {
204
234
  margin: 0;
205
- font-family: var(--component-heading-font-family-heading);
206
- font-weight: var(--component-heading-font-weight-heading);
207
- color: var(--semantic-color-text-default);
208
235
  }
209
236
 
210
- .a1-message-empty--page .a1-message-empty__title { font-size: var(--semantic-font-size-heading-sm); }
211
- .a1-message-empty--section .a1-message-empty__title { font-size: var(--semantic-font-size-heading-xs); }
212
- .a1-message-empty--card .a1-message-empty__title { font-size: var(--semantic-font-size-body-sm); }
213
-
214
237
  .a1-message-empty__description {
215
238
  margin: 0;
216
- font-family: var(--component-paragraph-font-family);
217
- color: var(--semantic-color-text-muted);
218
239
  }
219
240
 
220
- .a1-message-empty--page .a1-message-empty__description { font-size: var(--semantic-font-size-body-md); }
221
- .a1-message-empty--section .a1-message-empty__description { font-size: var(--semantic-font-size-body-sm); }
222
- .a1-message-empty--card .a1-message-empty__description { font-size: var(--semantic-font-size-body-xs); }
223
-
224
241
  .a1-message-empty__action {
225
242
  margin-top: var(--base-spacing-4);
226
243
  }
@@ -47,7 +47,7 @@ export function Notification({
47
47
  return (
48
48
  <span className="a1-notification-root">
49
49
  {children}
50
- <span className={classes} aria-label={ariaLabel}>
50
+ <span className={classes} role={ariaLabel ? "img" : undefined} aria-label={ariaLabel}>
51
51
  {content}
52
52
  </span>
53
53
  </span>
@@ -5,7 +5,10 @@ export function PageLayout({
5
5
  footer,
6
6
  sidebar,
7
7
  sidebarPlacement = "start",
8
+ aside,
9
+ asidePlacement = "end",
8
10
  stickyHeader = false,
11
+ viewportHeight = false,
9
12
  className = "",
10
13
  children,
11
14
  ...props
@@ -13,7 +16,9 @@ export function PageLayout({
13
16
  const rootClasses = [
14
17
  "a1-page-layout",
15
18
  stickyHeader && "a1-page-layout--sticky-header",
19
+ viewportHeight && "a1-page-layout--viewport-height",
16
20
  sidebar && `a1-page-layout--sidebar-${sidebarPlacement}`,
21
+ aside && `a1-page-layout--aside-${asidePlacement}`,
17
22
  className,
18
23
  ]
19
24
  .filter(Boolean)
@@ -29,7 +34,17 @@ export function PageLayout({
29
34
  {sidebar && (
30
35
  <aside className="a1-page-layout__sidebar">{sidebar}</aside>
31
36
  )}
32
- <main className="a1-page-layout__main">{children}</main>
37
+ <div className="a1-page-layout__content">
38
+ {aside && asidePlacement === "start" && (
39
+ <aside className="a1-page-layout__aside">{aside}</aside>
40
+ )}
41
+ <main className="a1-page-layout__main">
42
+ <div className="a1-page-layout__main-scroll" tabIndex={0}>{children}</div>
43
+ </main>
44
+ {aside && asidePlacement !== "start" && (
45
+ <aside className="a1-page-layout__aside">{aside}</aside>
46
+ )}
47
+ </div>
33
48
  </div>
34
49
 
35
50
  {footer && (
@@ -2,6 +2,7 @@
2
2
  display: flex;
3
3
  flex-direction: column;
4
4
  min-height: 100vh;
5
+ min-height: 100dvh;
5
6
  background: var(--semantic-color-surface-page);
6
7
  color: var(--semantic-color-text-default);
7
8
  }
@@ -20,7 +21,6 @@
20
21
  display: flex;
21
22
  flex: 1 1 auto;
22
23
  min-height: 0;
23
- gap: var(--component-page-layout-gap);
24
24
  }
25
25
 
26
26
  .a1-page-layout--sidebar-start .a1-page-layout__body {
@@ -41,21 +41,114 @@
41
41
  width: auto;
42
42
  }
43
43
 
44
+ .a1-page-layout__content {
45
+ display: flex;
46
+ flex-direction: column;
47
+ flex: 1 1 auto;
48
+ min-height: 0;
49
+ min-width: 0;
50
+ }
51
+
52
+ .a1-page-layout--aside-start .a1-page-layout__content,
53
+ .a1-page-layout--aside-end .a1-page-layout__content {
54
+ display: grid;
55
+ grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
56
+ align-items: start;
57
+ }
58
+
59
+ .a1-page-layout--aside-start .a1-page-layout__content {
60
+ grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
61
+ }
62
+
63
+ .a1-page-layout--aside-start .a1-page-layout__aside {
64
+ grid-column: 1;
65
+ }
66
+
67
+ .a1-page-layout--aside-start .a1-page-layout__main {
68
+ grid-column: 2;
69
+ }
70
+
44
71
  .a1-page-layout__main {
45
72
  flex: 1 1 0;
73
+ display: flex;
74
+ flex-direction: column;
75
+ min-height: 0;
76
+ min-width: 0;
77
+ }
78
+
79
+ .a1-page-layout__main-scroll {
80
+ flex: 1 1 0;
81
+ min-height: 0;
46
82
  min-width: 0;
47
83
  }
48
84
 
85
+ .a1-page-layout__aside {
86
+ min-width: 0;
87
+ /* Stretch to match the main column height so that sticky children
88
+ have a tall-enough containing block to actually stick. */
89
+ align-self: stretch;
90
+ }
91
+
49
92
  .a1-page-layout__footer {
50
93
  flex-shrink: 0;
51
94
  }
52
95
 
53
- @media (max-width: 640px) {
54
- .a1-page-layout__body {
55
- flex-direction: column !important;
96
+ /* ── Viewport-height mode (editor / app shell layouts) ───────────────────── */
97
+ /* Each panel scrolls independently; the outer container never overflows. */
98
+
99
+ .a1-page-layout--viewport-height {
100
+ height: 100vh;
101
+ height: 100dvh;
102
+ overflow: hidden;
103
+ }
104
+
105
+ .a1-page-layout--viewport-height .a1-page-layout__body {
106
+ overflow: hidden;
107
+ }
108
+
109
+ .a1-page-layout--viewport-height .a1-page-layout__sidebar {
110
+ overflow-y: auto;
111
+ }
112
+
113
+ .a1-page-layout--viewport-height .a1-page-layout__content {
114
+ overflow: hidden;
115
+ }
116
+
117
+ .a1-page-layout--viewport-height.a1-page-layout--aside-end .a1-page-layout__content,
118
+ .a1-page-layout--viewport-height.a1-page-layout--aside-start .a1-page-layout__content {
119
+ align-items: stretch;
120
+ }
121
+
122
+ .a1-page-layout--viewport-height .a1-page-layout__aside {
123
+ overflow-y: auto;
124
+ }
125
+
126
+ .a1-page-layout--viewport-height .a1-page-layout__main {
127
+ overflow: hidden;
128
+ }
129
+
130
+ .a1-page-layout--viewport-height .a1-page-layout__main-scroll {
131
+ overflow-y: auto;
132
+ overscroll-behavior-y: contain;
133
+ -webkit-overflow-scrolling: touch;
134
+ }
135
+
136
+ @media (--bp-sm-down) {
137
+ .a1-page-layout.a1-page-layout .a1-page-layout__body {
138
+ flex-direction: column;
56
139
  }
57
140
 
58
141
  .a1-page-layout__sidebar {
59
142
  width: 100%;
60
143
  }
144
+
145
+ .a1-page-layout--aside-start .a1-page-layout__content,
146
+ .a1-page-layout--aside-end .a1-page-layout__content {
147
+ grid-template-columns: minmax(0, 1fr);
148
+ }
149
+
150
+ .a1-page-layout--aside-start .a1-page-layout__aside,
151
+ .a1-page-layout--aside-start .a1-page-layout__main {
152
+ grid-column: auto;
153
+ }
61
154
  }