@arclux/arc-ui 1.2.0 → 1.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arclux/arc-ui",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "ARC UI — Lit Web Components implementing the Arclight design system.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -149,6 +149,7 @@
149
149
  "./menu-item": "./src/shared/menu-item.js",
150
150
  "./menu-divider": "./src/shared/menu-divider.js",
151
151
  "./tokens": "./src/tokens.js",
152
+ "./tokens.css": "./src/tokens.css",
152
153
  "./icons/phosphor": "./src/icons/phosphor.js",
153
154
  "./icons/lucide": "./src/icons/lucide.js",
154
155
  "./icons/types": "./src/icons/types.d.ts"
@@ -0,0 +1,50 @@
1
+ import { css } from 'lit';
2
+
3
+ /**
4
+ * Shared button variant styles for primary / secondary / ghost.
5
+ *
6
+ * Each consuming component still defines:
7
+ * - Its own default variant (Button → primary, IconButton → ghost)
8
+ * - Its own :active transform scale
9
+ * - Sizes, layout, and component-specific rules
10
+ */
11
+ export const buttonVariantStyles = css`
12
+ /* ── Primary ── */
13
+ :host([variant="primary"]) .btn {
14
+ background: var(--accent-primary);
15
+ color: var(--bg-deep);
16
+ border-color: var(--accent-primary);
17
+ }
18
+ :host([variant="primary"]) .btn:hover {
19
+ box-shadow: var(--glow-primary);
20
+ }
21
+
22
+ /* ── Secondary ── */
23
+ :host([variant="secondary"]) .btn {
24
+ background: transparent;
25
+ color: var(--text-primary);
26
+ border-color: var(--border-default);
27
+ }
28
+ :host([variant="secondary"]) .btn:hover {
29
+ border-color: var(--accent-primary);
30
+ color: var(--accent-primary);
31
+ box-shadow: 0 0 20px var(--accent-primary-ring);
32
+ }
33
+
34
+ /* ── Ghost ── */
35
+ :host([variant="ghost"]) .btn {
36
+ background: transparent;
37
+ color: var(--text-muted);
38
+ border-color: transparent;
39
+ }
40
+ :host([variant="ghost"]) .btn:hover {
41
+ color: var(--text-primary);
42
+ background: var(--bg-hover);
43
+ }
44
+
45
+ /* ── Focus ── */
46
+ .btn:focus-visible { outline: none; box-shadow: var(--focus-glow); }
47
+
48
+ /* ── Disabled ── */
49
+ :host([disabled]) .btn { opacity: 0.4; cursor: not-allowed; pointer-events: none; }
50
+ `;
@@ -0,0 +1,42 @@
1
+ import { css } from 'lit';
2
+
3
+ /**
4
+ * Shared card shell + hover styles for Card / FeatureCard.
5
+ *
6
+ * Provides the 1px-padding border trick, hover gradient border,
7
+ * inner box-shadow glow, and focus ring. Each consumer keeps its
8
+ * own inner layout, content, and responsive overrides.
9
+ */
10
+ export const cardHoverStyles = css`
11
+ .card {
12
+ position: relative;
13
+ border-radius: var(--radius-lg);
14
+ padding: 1px;
15
+ background: var(--border-subtle);
16
+ transition: background var(--transition-slow);
17
+ text-decoration: none;
18
+ display: flex;
19
+ flex-direction: column;
20
+ }
21
+
22
+ .card:hover {
23
+ background: linear-gradient(135deg, rgba(var(--accent-primary-rgb),0.3), rgba(var(--accent-secondary-rgb),0.15), var(--border-default));
24
+ }
25
+
26
+ .card__inner {
27
+ position: relative;
28
+ background: var(--bg-card);
29
+ border-radius: calc(var(--radius-lg) - 1px);
30
+ flex: 1;
31
+ min-height: 0;
32
+ display: flex;
33
+ flex-direction: column;
34
+ transition: box-shadow var(--transition-slow);
35
+ }
36
+
37
+ .card:hover .card__inner {
38
+ box-shadow: inset 0 1px 0 var(--bg-hover), var(--glow-card-hover);
39
+ }
40
+
41
+ .card:focus-visible { outline: none; box-shadow: var(--focus-glow); border-radius: var(--radius-lg); }
42
+ `;
@@ -4,6 +4,7 @@ import { tokenStyles } from '../shared-styles.js';
4
4
  export class ArcBadge extends LitElement {
5
5
  static properties = {
6
6
  variant: { type: String, reflect: true },
7
+ color: { type: String },
7
8
  };
8
9
 
9
10
  static styles = [
@@ -15,11 +16,11 @@ export class ArcBadge extends LitElement {
15
16
  display: inline-flex;
16
17
  align-items: center;
17
18
  gap: var(--space-xs);
18
- font-family: var(--font-accent);
19
- font-weight: 600;
19
+ font-family: var(--font-mono);
20
+ font-weight: 500;
20
21
  font-size: var(--text-xs);
21
- letter-spacing: 2px;
22
- text-transform: uppercase;
22
+ letter-spacing: normal;
23
+ text-transform: none;
23
24
  color: var(--text-muted);
24
25
  padding: var(--space-xs) var(--space-sm);
25
26
  border: 1px solid var(--border-default);
@@ -88,10 +89,18 @@ export class ArcBadge extends LitElement {
88
89
  constructor() {
89
90
  super();
90
91
  this.variant = 'default';
92
+ this.color = '';
91
93
  }
92
94
 
93
95
  render() {
94
- return html`<span class="badge" part="badge"><slot></slot></span>`;
96
+ const colorStyle = this.color
97
+ ? `border-color: rgba(${this.color}, 0.2); color: rgb(${this.color}); background: rgba(${this.color}, 0.06);`
98
+ : '';
99
+
100
+ return html`<span class="badge" part="badge" style=${colorStyle}
101
+ @mouseenter=${this.color ? (e) => { e.currentTarget.style.boxShadow = `0 0 12px rgba(${this.color}, 0.15)`; } : null}
102
+ @mouseleave=${this.color ? (e) => { e.currentTarget.style.boxShadow = ''; } : null}
103
+ ><slot></slot></span>`;
95
104
  }
96
105
  }
97
106
 
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html, css } from 'lit';
2
2
  import { tokenStyles } from '../shared-styles.js';
3
+ import { statusVars } from '../status-styles.js';
3
4
  import './icon.js';
4
5
 
5
6
  export class ArcCallout extends LitElement {
@@ -9,6 +10,7 @@ export class ArcCallout extends LitElement {
9
10
 
10
11
  static styles = [
11
12
  tokenStyles,
13
+ statusVars,
12
14
  css`
13
15
  :host { display: block; }
14
16
 
@@ -18,8 +20,8 @@ export class ArcCallout extends LitElement {
18
20
  gap: var(--space-sm);
19
21
  padding: var(--space-md) var(--space-lg);
20
22
  border-radius: var(--radius-md);
21
- border: 1px solid var(--border-default);
22
- background: var(--bg-card);
23
+ border: 1px solid rgba(var(--_status-rgb), 0.12);
24
+ background: rgba(var(--_status-rgb), 0.04);
23
25
  font-family: var(--font-body);
24
26
  font-size: var(--text-sm);
25
27
  line-height: 1.7;
@@ -36,6 +38,7 @@ export class ArcCallout extends LitElement {
36
38
  flex-shrink: 0;
37
39
  display: flex;
38
40
  align-items: center;
41
+ color: var(--_status-color);
39
42
  }
40
43
 
41
44
  .callout__label {
@@ -44,44 +47,13 @@ export class ArcCallout extends LitElement {
44
47
  font-weight: 600;
45
48
  letter-spacing: 1.5px;
46
49
  text-transform: uppercase;
50
+ color: var(--_status-color);
47
51
  }
48
52
 
49
53
  .callout__content {
50
54
  min-width: 0;
51
55
  }
52
56
 
53
- /* ── Info ── */
54
- :host([variant="info"]) .callout {
55
- border-color: rgba(var(--accent-primary-rgb), 0.12);
56
- background: rgba(var(--accent-primary-rgb), 0.04);
57
- }
58
- :host([variant="info"]) .callout__icon { color: var(--accent-primary); }
59
- :host([variant="info"]) .callout__label { color: var(--accent-primary); }
60
-
61
- /* ── Warning ── */
62
- :host([variant="warning"]) .callout {
63
- border-color: rgba(var(--color-warning-rgb), 0.15);
64
- background: rgba(var(--color-warning-rgb), 0.04);
65
- }
66
- :host([variant="warning"]) .callout__icon { color: var(--color-warning); }
67
- :host([variant="warning"]) .callout__label { color: var(--color-warning); }
68
-
69
- /* ── Tip ── */
70
- :host([variant="tip"]) .callout {
71
- border-color: rgba(var(--color-success-rgb), 0.15);
72
- background: rgba(var(--color-success-rgb), 0.04);
73
- }
74
- :host([variant="tip"]) .callout__icon { color: var(--color-success); }
75
- :host([variant="tip"]) .callout__label { color: var(--color-success); }
76
-
77
- /* ── Danger ── */
78
- :host([variant="danger"]) .callout {
79
- border-color: rgba(var(--color-error-rgb), 0.15);
80
- background: rgba(var(--color-error-rgb), 0.04);
81
- }
82
- :host([variant="danger"]) .callout__icon { color: var(--color-error); }
83
- :host([variant="danger"]) .callout__label { color: var(--color-error); }
84
-
85
57
  @media (prefers-reduced-motion: reduce) {
86
58
  :host *,
87
59
  :host *::before,
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html, css } from 'lit';
2
2
  import { tokenStyles } from '../shared-styles.js';
3
+ import { cardHoverStyles } from '../card-styles.js';
3
4
 
4
5
  export class ArcCard extends LitElement {
5
6
  static properties = {
@@ -9,43 +10,24 @@ export class ArcCard extends LitElement {
9
10
 
10
11
  static styles = [
11
12
  tokenStyles,
13
+ cardHoverStyles,
12
14
  css`
13
15
  :host { display: block; }
14
16
 
15
- .card {
16
- position: relative;
17
- border-radius: var(--radius-lg);
18
- padding: 1px;
17
+ .card { height: 100%; }
18
+
19
+ /* Suppress hover effect when no href */
20
+ :host(:not([href])) .card:hover {
19
21
  background: var(--border-subtle);
20
- transition: background var(--transition-slow);
21
- text-decoration: none;
22
- display: flex;
23
- flex-direction: column;
24
- height: 100%;
25
22
  }
26
-
27
- :host([href]) .card:hover {
28
- background: linear-gradient(135deg, rgba(var(--accent-primary-rgb),0.3), rgba(var(--accent-secondary-rgb),0.15), var(--border-default));
23
+ :host(:not([href])) .card:hover .card__inner {
24
+ box-shadow: none;
29
25
  }
30
26
 
31
27
  .card__inner {
32
- position: relative;
33
- background: var(--bg-card);
34
- border-radius: calc(var(--radius-lg) - 1px);
35
28
  padding: var(--space-xl) var(--space-lg);
36
- flex: 1;
37
- min-height: 0;
38
- display: flex;
39
- flex-direction: column;
40
- transition: box-shadow var(--transition-slow);
41
29
  }
42
30
 
43
- .card:hover .card__inner {
44
- box-shadow: inset 0 1px 0 var(--bg-hover), var(--glow-card-hover);
45
- }
46
-
47
- .card:focus-visible { outline: none; box-shadow: var(--focus-glow); border-radius: var(--radius-lg); }
48
-
49
31
  .card__body {
50
32
  flex: 1;
51
33
  }
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html, css } from 'lit';
2
2
  import { tokenStyles } from '../shared-styles.js';
3
+ import { cardHoverStyles } from '../card-styles.js';
3
4
 
4
5
  export class ArcFeatureCard extends LitElement {
5
6
  static properties = {
@@ -11,42 +12,18 @@ export class ArcFeatureCard extends LitElement {
11
12
 
12
13
  static styles = [
13
14
  tokenStyles,
15
+ cardHoverStyles,
14
16
  css`
15
17
  :host { display: block; height: 100%; }
16
18
 
17
- .card {
18
- position: relative;
19
- border-radius: var(--radius-lg);
20
- height: 100%;
21
- padding: 1px;
22
- background: var(--border-subtle);
23
- transition: background var(--transition-slow);
24
- text-decoration: none;
25
- display: flex;
26
- flex-direction: column;
27
- }
28
-
29
- .card:hover {
30
- background: linear-gradient(135deg, rgba(var(--accent-primary-rgb),0.3), rgba(var(--accent-secondary-rgb),0.15), var(--border-default));
31
- }
19
+ .card { height: 100%; }
32
20
 
33
21
  .card__inner {
34
- position: relative;
35
- background: var(--bg-card);
36
- border-radius: calc(var(--radius-lg) - 1px);
37
22
  padding: var(--space-xl) var(--space-lg);
38
- display: flex;
39
- flex-direction: column;
40
23
  gap: var(--space-md);
41
- flex: 1;
42
- transition: box-shadow var(--transition-slow);
43
24
  z-index: 1;
44
25
  }
45
26
 
46
- .card:hover .card__inner {
47
- box-shadow: inset 0 1px 0 var(--bg-hover), var(--glow-card-hover);
48
- }
49
-
50
27
  .card__icon {
51
28
  width: 44px;
52
29
  height: 44px;
@@ -96,8 +73,6 @@ export class ArcFeatureCard extends LitElement {
96
73
 
97
74
  .card:hover .card__rule { opacity: 0.5; width: 48px; }
98
75
 
99
- .card:focus-visible { outline: none; box-shadow: var(--focus-glow); border-radius: var(--radius-lg); }
100
-
101
76
  @media (max-width: 768px) {
102
77
  .card__inner { padding: var(--space-lg) var(--space-md); }
103
78
  }
@@ -25,13 +25,12 @@ export class ArcTag extends LitElement {
25
25
  letter-spacing: 2px;
26
26
  text-transform: uppercase;
27
27
  color: var(--text-muted);
28
- padding: var(--space-xs) calc(var(--space-sm) + var(--space-xs));
28
+ padding: var(--space-sm) calc(var(--space-sm) + var(--space-xs));
29
29
  border: 1px solid var(--border-default);
30
30
  border-radius: var(--radius-full);
31
31
  background: var(--bg-hover);
32
32
  transition: box-shadow var(--transition-base), border-color var(--transition-base);
33
33
  line-height: 1.4;
34
- min-height: var(--touch-min);
35
34
  }
36
35
 
37
36
  :host([variant="primary"]) .tag {
@@ -1,5 +1,7 @@
1
1
  import { LitElement, html, css } from 'lit';
2
2
  import { tokenStyles } from '../shared-styles.js';
3
+ import { statusVars } from '../status-styles.js';
4
+ import { getStatusIcon } from '../status-utils.js';
3
5
 
4
6
  export class ArcAlert extends LitElement {
5
7
  static properties = {
@@ -10,6 +12,7 @@ export class ArcAlert extends LitElement {
10
12
 
11
13
  static styles = [
12
14
  tokenStyles,
15
+ statusVars,
13
16
  css`
14
17
  :host { display: block; }
15
18
 
@@ -32,24 +35,8 @@ export class ArcAlert extends LitElement {
32
35
  left: 0;
33
36
  right: 0;
34
37
  height: 2px;
35
- background: var(--gradient-divider);
36
- }
37
-
38
- :host([variant="info"]) .alert::before {
39
- background: linear-gradient(90deg, transparent, var(--accent-primary), transparent);
40
- box-shadow: 0 0 12px rgba(var(--accent-primary-rgb), 0.15);
41
- }
42
- :host([variant="success"]) .alert::before {
43
- background: linear-gradient(90deg, transparent, var(--color-success), transparent);
44
- box-shadow: 0 0 12px rgba(var(--color-success-rgb), 0.15);
45
- }
46
- :host([variant="warning"]) .alert::before {
47
- background: linear-gradient(90deg, transparent, var(--color-warning), transparent);
48
- box-shadow: 0 0 12px rgba(var(--color-warning-rgb), 0.15);
49
- }
50
- :host([variant="error"]) .alert::before {
51
- background: linear-gradient(90deg, transparent, var(--color-error), transparent);
52
- box-shadow: 0 0 12px rgba(var(--color-error-rgb), 0.15);
38
+ background: linear-gradient(90deg, transparent, var(--_status-color), transparent);
39
+ box-shadow: 0 0 12px rgba(var(--_status-rgb), 0.15);
53
40
  }
54
41
 
55
42
  .alert__icon-wrap {
@@ -62,31 +49,10 @@ export class ArcAlert extends LitElement {
62
49
  border-radius: var(--radius-md);
63
50
  font-size: var(--text-md);
64
51
  transition: box-shadow var(--transition-base);
65
- }
66
-
67
- :host([variant="info"]) .alert__icon-wrap {
68
- background: rgba(var(--accent-primary-rgb), 0.08);
69
- border: 1px solid rgba(var(--accent-primary-rgb), 0.15);
70
- color: var(--accent-primary);
71
- box-shadow: 0 0 16px rgba(var(--accent-primary-rgb), 0.1);
72
- }
73
- :host([variant="success"]) .alert__icon-wrap {
74
- background: rgba(var(--color-success-rgb), 0.08);
75
- border: 1px solid rgba(var(--color-success-rgb), 0.15);
76
- color: var(--color-success);
77
- box-shadow: 0 0 16px rgba(var(--color-success-rgb), 0.1);
78
- }
79
- :host([variant="warning"]) .alert__icon-wrap {
80
- background: rgba(var(--color-warning-rgb), 0.08);
81
- border: 1px solid rgba(var(--color-warning-rgb), 0.15);
82
- color: var(--color-warning);
83
- box-shadow: 0 0 16px rgba(var(--color-warning-rgb), 0.1);
84
- }
85
- :host([variant="error"]) .alert__icon-wrap {
86
- background: rgba(var(--color-error-rgb), 0.08);
87
- border: 1px solid rgba(var(--color-error-rgb), 0.15);
88
- color: var(--color-error);
89
- box-shadow: 0 0 16px rgba(var(--color-error-rgb), 0.1);
52
+ background: rgba(var(--_status-rgb), 0.08);
53
+ border: 1px solid rgba(var(--_status-rgb), 0.15);
54
+ color: var(--_status-color);
55
+ box-shadow: 0 0 16px rgba(var(--_status-rgb), 0.1);
90
56
  }
91
57
 
92
58
  .alert__body { flex: 1; min-width: 0; }
@@ -154,15 +120,6 @@ export class ArcAlert extends LitElement {
154
120
  this.heading = '';
155
121
  }
156
122
 
157
- _getIcon() {
158
- switch (this.variant) {
159
- case 'success': return '\u2713';
160
- case 'warning': return '\u26A0';
161
- case 'error': return '\u2717';
162
- default: return '\u2139';
163
- }
164
- }
165
-
166
123
  _dismiss() {
167
124
  this.dispatchEvent(new CustomEvent('arc-dismiss', { bubbles: true, composed: true }));
168
125
  this.style.display = 'none';
@@ -171,7 +128,7 @@ export class ArcAlert extends LitElement {
171
128
  render() {
172
129
  return html`
173
130
  <div class="alert" role="alert" part="alert">
174
- <div class="alert__icon-wrap" part="icon">${this._getIcon()}</div>
131
+ <div class="alert__icon-wrap" part="icon">${getStatusIcon(this.variant)}</div>
175
132
  <div class="alert__body">
176
133
  ${this.heading ? html`<p class="alert__heading" part="heading">${this.heading}</p>` : ''}
177
134
  <div class="alert__content" part="content"><slot></slot></div>
@@ -1,5 +1,18 @@
1
1
  import { LitElement, html, css } from 'lit';
2
2
  import { tokenStyles } from '../shared-styles.js';
3
+ import { getStatusIcon } from '../status-utils.js';
4
+
5
+ /** Returns inline style string setting --_status-color/rgb for a given variant */
6
+ function statusStyle(variant) {
7
+ const map = {
8
+ info: { color: 'var(--accent-primary)', rgb: 'var(--accent-primary-rgb)' },
9
+ success: { color: 'var(--color-success)', rgb: 'var(--color-success-rgb)' },
10
+ warning: { color: 'var(--color-warning)', rgb: 'var(--color-warning-rgb)' },
11
+ error: { color: 'var(--color-error)', rgb: 'var(--color-error-rgb)' },
12
+ };
13
+ const v = map[variant] || map.info;
14
+ return `--_status-color:${v.color};--_status-rgb:${v.rgb}`;
15
+ }
3
16
 
4
17
  export class ArcToast extends LitElement {
5
18
  static properties = {
@@ -71,33 +84,16 @@ export class ArcToast extends LitElement {
71
84
  left: 0;
72
85
  right: 0;
73
86
  height: 2px;
74
- background: var(--gradient-divider);
75
- }
76
-
77
- .toast--info::after {
78
- background: linear-gradient(90deg, transparent, var(--accent-primary), transparent);
79
- }
80
- .toast--success::after {
81
- background: linear-gradient(90deg, transparent, var(--color-success), transparent);
82
- }
83
- .toast--warning::after {
84
- background: linear-gradient(90deg, transparent, var(--color-warning), transparent);
85
- }
86
- .toast--error::after {
87
- background: linear-gradient(90deg, transparent, var(--color-error), transparent);
87
+ background: linear-gradient(90deg, transparent, var(--_status-color), transparent);
88
88
  }
89
89
 
90
90
  .toast__icon {
91
91
  font-size: var(--text-sm);
92
92
  flex-shrink: 0;
93
93
  line-height: 1;
94
+ color: var(--_status-color);
94
95
  }
95
96
 
96
- .toast--info .toast__icon { color: var(--accent-primary); }
97
- .toast--success .toast__icon { color: var(--color-success); }
98
- .toast--warning .toast__icon { color: var(--color-warning); }
99
- .toast--error .toast__icon { color: var(--color-error); }
100
-
101
97
  .toast__message { flex: 1; }
102
98
 
103
99
  .toast__dismiss {
@@ -149,15 +145,6 @@ export class ArcToast extends LitElement {
149
145
  this._counter = 0;
150
146
  }
151
147
 
152
- _getIcon(variant) {
153
- switch (variant) {
154
- case 'success': return '\u2713';
155
- case 'warning': return '\u26A0';
156
- case 'error': return '\u2717';
157
- default: return '\u2139';
158
- }
159
- }
160
-
161
148
  show({ message, variant = 'info', duration }) {
162
149
  const id = ++this._counter;
163
150
  const dur = duration ?? this.duration;
@@ -193,8 +180,8 @@ export class ArcToast extends LitElement {
193
180
  return html`
194
181
  <div class="toast-container" role="status" aria-live="polite" aria-atomic="false" part="container">
195
182
  ${this._toasts.map(t => html`
196
- <div class="toast toast--${t.variant}" data-toast-id=${t.id} part="toast">
197
- <span class="toast__icon" aria-hidden="true">${this._getIcon(t.variant)}</span>
183
+ <div class="toast" style=${statusStyle(t.variant)} data-toast-id=${t.id} part="toast">
184
+ <span class="toast__icon" aria-hidden="true">${getStatusIcon(t.variant)}</span>
198
185
  <span class="toast__message">${t.message}</span>
199
186
  <button class="toast__dismiss" @click=${() => this._dismiss(t.id)} aria-label="Dismiss">&times;</button>
200
187
  </div>
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html, css } from 'lit';
2
2
  import { tokenStyles } from '../shared-styles.js';
3
+ import { buttonVariantStyles } from '../button-styles.js';
3
4
 
4
5
  export class ArcButton extends LitElement {
5
6
  static properties = {
@@ -14,6 +15,7 @@ export class ArcButton extends LitElement {
14
15
 
15
16
  static styles = [
16
17
  tokenStyles,
18
+ buttonVariantStyles,
17
19
  css`
18
20
  :host { display: inline-flex; }
19
21
  :host([disabled]) { pointer-events: none; }
@@ -48,55 +50,26 @@ export class ArcButton extends LitElement {
48
50
  :host([size="md"]) .btn { font-size: var(--text-xs); padding: var(--space-sm) var(--space-lg); }
49
51
  :host([size="lg"]) .btn { font-size: var(--text-xs); padding: var(--space-md) var(--space-xl); letter-spacing: 3px; }
50
52
 
51
- /* Primary */
52
- :host(:not([variant])) .btn,
53
- :host([variant="primary"]) .btn {
53
+ /* Default → primary */
54
+ :host(:not([variant])) .btn {
54
55
  background: var(--accent-primary);
55
56
  color: var(--bg-deep);
56
57
  border-color: var(--accent-primary);
57
58
  }
58
- :host(:not([variant])) .btn:hover,
59
- :host([variant="primary"]) .btn:hover { box-shadow: var(--glow-primary); }
59
+ :host(:not([variant])) .btn:hover { box-shadow: var(--glow-primary); }
60
+
61
+ /* :active scale */
60
62
  :host(:not([variant])) .btn:active,
61
63
  :host([variant="primary"]) .btn:active { transform: scale(0.97); box-shadow: 0 0 8px rgba(var(--accent-primary-rgb),0.5); }
62
-
63
- /* Secondary */
64
- :host([variant="secondary"]) .btn {
65
- background: transparent;
66
- color: var(--text-primary);
67
- border-color: var(--border-default);
68
- }
69
- :host([variant="secondary"]) .btn:hover {
70
- border-color: var(--accent-primary);
71
- color: var(--accent-primary);
72
- box-shadow: 0 0 20px var(--accent-primary-ring);
73
- }
74
64
  :host([variant="secondary"]) .btn:active {
75
65
  transform: scale(0.97);
76
66
  background: rgba(var(--accent-primary-rgb),0.05);
77
67
  }
78
-
79
- /* Ghost */
80
- :host([variant="ghost"]) .btn {
81
- background: transparent;
82
- color: var(--text-muted);
83
- border-color: transparent;
84
- }
85
- :host([variant="ghost"]) .btn:hover {
86
- color: var(--text-primary);
87
- background: var(--bg-hover);
88
- }
89
68
  :host([variant="ghost"]) .btn:active {
90
69
  transform: scale(0.97);
91
70
  background: var(--bg-elevated);
92
71
  }
93
72
 
94
- /* Focus */
95
- .btn:focus-visible { outline: none; box-shadow: var(--focus-glow); }
96
-
97
- /* Disabled */
98
- :host([disabled]) .btn { opacity: 0.4; cursor: not-allowed; pointer-events: none; }
99
-
100
73
  /* Prefix / Suffix */
101
74
  .btn__prefix,
102
75
  .btn__suffix {
package/src/input/chip.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html, css } from 'lit';
2
2
  import { tokenStyles } from '../shared-styles.js';
3
+ import '../content/tag.js';
3
4
 
4
5
  export class ArcChip extends LitElement {
5
6
  static properties = {
@@ -11,56 +12,15 @@ export class ArcChip extends LitElement {
11
12
  static styles = [
12
13
  tokenStyles,
13
14
  css`
14
- :host { display: inline-flex; }
15
- :host([disabled]) { pointer-events: none; opacity: 0.4; }
16
-
17
- .chip {
15
+ :host {
18
16
  display: inline-flex;
19
- align-items: center;
20
- gap: var(--space-xs);
21
- font-family: var(--font-accent);
22
- font-weight: 600;
23
- font-size: var(--text-xs);
24
- letter-spacing: 2px;
25
- text-transform: uppercase;
26
- color: var(--text-muted);
27
- padding: calc(var(--space-xs) + 2px) var(--space-md);
28
- border: 1px solid var(--border-default);
29
- border-radius: var(--radius-full);
30
- background: var(--bg-hover);
31
17
  cursor: pointer;
32
18
  user-select: none;
33
- transition:
34
- background var(--transition-base),
35
- border-color var(--transition-base),
36
- color var(--transition-base),
37
- box-shadow var(--transition-base);
38
- line-height: 1.4;
39
- outline: none;
40
- }
41
-
42
- .chip:hover {
43
- border-color: var(--border-bright);
44
- background: var(--bg-elevated);
45
- }
46
-
47
- :host([selected]) .chip {
48
- border-color: var(--accent-primary);
49
- color: var(--accent-primary);
50
- background: var(--accent-primary-subtle);
51
- box-shadow: 0 0 12px rgba(var(--accent-primary-rgb), 0.15);
52
- }
53
-
54
- :host([selected]) .chip:hover {
55
- box-shadow: 0 0 16px rgba(var(--accent-primary-rgb), 0.25);
56
- }
57
-
58
- .chip:focus-visible {
59
- box-shadow: var(--focus-glow);
60
19
  }
20
+ :host([disabled]) { pointer-events: none; }
61
21
 
62
- :host([selected]) .chip:focus-visible {
63
- box-shadow: var(--focus-glow), 0 0 12px rgba(var(--accent-primary-rgb), 0.15);
22
+ arc-tag {
23
+ pointer-events: none;
64
24
  }
65
25
 
66
26
  @media (prefers-reduced-motion: reduce) {
@@ -101,8 +61,10 @@ export class ArcChip extends LitElement {
101
61
 
102
62
  render() {
103
63
  return html`
104
- <span
105
- class="chip"
64
+ <arc-tag
65
+ variant=${this.selected ? 'primary' : 'default'}
66
+ ?disabled=${this.disabled}
67
+ exportparts="tag: chip, label"
106
68
  role="option"
107
69
  aria-selected=${this.selected ? 'true' : 'false'}
108
70
  aria-disabled=${this.disabled ? 'true' : 'false'}
@@ -112,7 +74,7 @@ export class ArcChip extends LitElement {
112
74
  part="chip"
113
75
  >
114
76
  <slot></slot>
115
- </span>
77
+ </arc-tag>
116
78
  `;
117
79
  }
118
80
  }
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html, css } from 'lit';
2
2
  import { tokenStyles } from '../shared-styles.js';
3
+ import { buttonVariantStyles } from '../button-styles.js';
3
4
  import '../content/icon.js';
4
5
 
5
6
  export class ArcIconButton extends LitElement {
@@ -16,6 +17,7 @@ export class ArcIconButton extends LitElement {
16
17
 
17
18
  static styles = [
18
19
  tokenStyles,
20
+ buttonVariantStyles,
19
21
  css`
20
22
  :host { display: inline-flex; }
21
23
  :host([disabled]) { pointer-events: none; }
@@ -69,59 +71,41 @@ export class ArcIconButton extends LitElement {
69
71
  :host([size="md"]) .btn--has-text { padding: var(--space-xs) var(--space-sm); font-size: var(--text-xs); }
70
72
  :host([size="lg"]) .btn--has-text { padding: var(--space-sm) var(--space-md); font-size: var(--text-xs); }
71
73
 
72
- /* Ghost (default) */
73
- :host(:not([variant])) .btn,
74
- :host([variant="ghost"]) .btn {
74
+ /* Default ghost */
75
+ :host(:not([variant])) .btn {
75
76
  background: transparent;
76
77
  color: var(--text-muted);
77
78
  border-color: transparent;
78
79
  }
79
- :host(:not([variant])) .btn:hover,
80
- :host([variant="ghost"]) .btn:hover {
80
+ :host(:not([variant])) .btn:hover {
81
81
  color: var(--text-primary);
82
82
  background: var(--bg-hover);
83
83
  }
84
+
85
+ /* :active scale */
84
86
  :host(:not([variant])) .btn:active,
85
87
  :host([variant="ghost"]) .btn:active {
86
88
  transform: scale(0.93);
87
89
  background: var(--bg-elevated);
88
90
  }
89
-
90
- /* Secondary */
91
- :host([variant="secondary"]) .btn {
92
- background: transparent;
93
- color: var(--text-secondary);
94
- border-color: var(--border-default);
95
- }
96
- :host([variant="secondary"]) .btn:hover {
97
- border-color: var(--accent-primary);
98
- color: var(--accent-primary);
99
- box-shadow: 0 0 16px var(--accent-primary-ring);
100
- }
101
91
  :host([variant="secondary"]) .btn:active {
102
92
  transform: scale(0.93);
103
93
  background: rgba(var(--accent-primary-rgb), 0.05);
104
94
  }
105
-
106
- /* Primary */
107
- :host([variant="primary"]) .btn {
108
- background: var(--accent-primary);
109
- color: var(--bg-deep);
110
- border-color: var(--accent-primary);
111
- }
112
- :host([variant="primary"]) .btn:hover {
113
- box-shadow: var(--glow-primary);
114
- }
115
95
  :host([variant="primary"]) .btn:active {
116
96
  transform: scale(0.93);
117
97
  box-shadow: 0 0 8px rgba(var(--accent-primary-rgb), 0.5);
118
98
  }
119
99
 
120
- /* Focus */
121
- .btn:focus-visible { outline: none; box-shadow: var(--focus-glow); }
100
+ /* IconButton secondary uses text-secondary instead of text-primary */
101
+ :host([variant="secondary"]) .btn {
102
+ color: var(--text-secondary);
103
+ }
122
104
 
123
- /* Disabled */
124
- :host([disabled]) .btn { opacity: 0.4; cursor: not-allowed; pointer-events: none; }
105
+ /* IconButton secondary hover uses smaller glow */
106
+ :host([variant="secondary"]) .btn:hover {
107
+ box-shadow: 0 0 16px var(--accent-primary-ring);
108
+ }
125
109
 
126
110
  @media (prefers-reduced-motion: reduce) {
127
111
  :host *,
@@ -81,11 +81,11 @@ export class ArcToggle extends LitElement {
81
81
  }
82
82
 
83
83
  .toggle__label {
84
- font-family: var(--font-accent);
85
- font-size: var(--text-xs);
86
- font-weight: 600;
87
- letter-spacing: 1px;
88
- text-transform: uppercase;
84
+ font-family: var(--font-body);
85
+ font-size: var(--body-size);
86
+ font-weight: 400;
87
+ letter-spacing: normal;
88
+ text-transform: none;
89
89
  color: var(--text-muted);
90
90
  user-select: none;
91
91
  }
@@ -31,15 +31,19 @@ export class ArcTopBar extends LitElement {
31
31
 
32
32
  .topbar {
33
33
  position: relative;
34
- display: flex;
35
- align-items: center;
36
34
  height: var(--nav-height);
37
35
  padding: 0 var(--space-lg);
38
36
  background: color-mix(in srgb, var(--bg-deep) 85%, transparent);
39
37
  backdrop-filter: blur(12px) saturate(130%);
40
38
  -webkit-backdrop-filter: blur(12px) saturate(130%);
41
39
  border-bottom: 1px solid var(--border-subtle);
40
+ }
41
+
42
+ .topbar__content {
43
+ display: flex;
44
+ align-items: center;
42
45
  gap: var(--space-md);
46
+ height: 100%;
43
47
  }
44
48
 
45
49
  .topbar__glow {
@@ -241,19 +245,21 @@ export class ArcTopBar extends LitElement {
241
245
 
242
246
  return html`
243
247
  <header class="topbar" part="topbar">
244
- ${menuLeft ? this._renderMenuButton() : ''}
245
- <a class="topbar__brand" href="/" part="brand">
246
- <slot name="logo"></slot>
247
- ${this.heading ? html`<span class="topbar__heading">${this.heading}</span>` : ''}
248
- <slot name="subtitle"></slot>
249
- </a>
250
- <div class="topbar__center" part="center">
251
- <slot name="center"></slot>
252
- </div>
253
- <div class="topbar__actions" part="actions">
254
- <slot name="actions"></slot>
248
+ <div class="topbar__content" part="content">
249
+ ${menuLeft ? this._renderMenuButton() : ''}
250
+ <a class="topbar__brand" href="/" part="brand">
251
+ <slot name="logo"></slot>
252
+ ${this.heading ? html`<span class="topbar__heading">${this.heading}</span>` : ''}
253
+ <slot name="subtitle"></slot>
254
+ </a>
255
+ <div class="topbar__center" part="center">
256
+ <slot name="center"></slot>
257
+ </div>
258
+ <div class="topbar__actions" part="actions">
259
+ <slot name="actions"></slot>
260
+ </div>
261
+ ${!menuLeft ? this._renderMenuButton() : ''}
255
262
  </div>
256
- ${!menuLeft ? this._renderMenuButton() : ''}
257
263
  <div class="topbar__glow"></div>
258
264
  </header>
259
265
  `;
@@ -16,6 +16,11 @@ export const tokenStyles = css`
16
16
  --font-accent: 'Tektur', system-ui, sans-serif;
17
17
  --font-mono: 'JetBrains Mono', ui-monospace, monospace;
18
18
 
19
+ --text-xs: 12px;
20
+ --text-sm: 14px;
21
+ --text-md: 15px;
22
+ --text-xl: clamp(22px, 2.5vw, 26px);
23
+
19
24
  --display-xl-size: clamp(36px, 5vw, 52px);
20
25
  --display-xl-weight: 500;
21
26
  --display-xl-spacing: -1px;
@@ -64,6 +69,9 @@ export const tokenStyles = css`
64
69
  --duration-enter: 500ms;
65
70
  --duration-exit: 300ms;
66
71
 
72
+ --focus-ring: 0 0 0 1px rgba(var(--accent-primary-rgb), 0.25);
73
+ --focus-glow: 0 0 0 1px rgba(var(--accent-primary-rgb), 0.2), 0 0 6px rgba(var(--accent-primary-rgb), 0.35), 0 0 16px rgba(var(--accent-primary-rgb), 0.2), 0 0 40px rgba(var(--accent-secondary-rgb), 0.12);
74
+
67
75
  --touch-min: 24px;
68
76
  --touch-pad: 4px;
69
77
 
@@ -0,0 +1,33 @@
1
+ import { css } from 'lit';
2
+
3
+ /**
4
+ * Shared status-variant CSS custom properties.
5
+ * Maps variant attributes on :host to --_status-color / --_status-rgb.
6
+ *
7
+ * Covers both Alert/Toast names (info, success, warning, error)
8
+ * and Callout aliases (note → info, tip → success, danger → error).
9
+ */
10
+ export const statusVars = css`
11
+ :host([variant="info"]),
12
+ :host([variant="note"]) {
13
+ --_status-color: var(--accent-primary);
14
+ --_status-rgb: var(--accent-primary-rgb);
15
+ }
16
+
17
+ :host([variant="success"]),
18
+ :host([variant="tip"]) {
19
+ --_status-color: var(--color-success);
20
+ --_status-rgb: var(--color-success-rgb);
21
+ }
22
+
23
+ :host([variant="warning"]) {
24
+ --_status-color: var(--color-warning);
25
+ --_status-rgb: var(--color-warning-rgb);
26
+ }
27
+
28
+ :host([variant="error"]),
29
+ :host([variant="danger"]) {
30
+ --_status-color: var(--color-error);
31
+ --_status-rgb: var(--color-error-rgb);
32
+ }
33
+ `;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Returns a unicode status icon for the given variant.
3
+ * Shared by Alert and Toast.
4
+ */
5
+ export function getStatusIcon(variant) {
6
+ switch (variant) {
7
+ case 'success': return '\u2713';
8
+ case 'warning': return '\u26A0';
9
+ case 'error': return '\u2717';
10
+ default: return '\u2139';
11
+ }
12
+ }
package/src/tokens.css ADDED
@@ -0,0 +1,322 @@
1
+ /* Generated from shared/tokens.js — do not edit by hand */
2
+
3
+ :root {
4
+
5
+ --bg-deep: rgb(3, 3, 7);
6
+ --bg-surface: rgb(10, 10, 15);
7
+ --bg-base: rgb(10, 10, 15);
8
+ --bg-card: rgb(13, 13, 18);
9
+ --bg-elevated: rgb(17, 17, 22);
10
+
11
+ --text-primary: rgb(232, 232, 236);
12
+ --text-secondary: rgb(138, 138, 150);
13
+ --text-muted: rgb(124, 124, 137);
14
+ --text-ghost: rgb(107, 107, 128);
15
+
16
+ --border-subtle: rgb(24, 24, 30);
17
+ --border-default: rgb(34, 34, 41);
18
+ --border-bright: rgb(51, 51, 64);
19
+
20
+ --accent-primary: rgb(77, 126, 247);
21
+ --accent-secondary: rgb(139, 92, 246);
22
+ --accent-primary-rgb: 77, 126, 247;
23
+ --accent-secondary-rgb: 139, 92, 246;
24
+ --text-primary-rgb: 232, 232, 236;
25
+ --text-muted-rgb: 124, 124, 137;
26
+ --color-error-rgb: 239, 68, 68;
27
+ --white-rgb: 255, 255, 255;
28
+ --black-rgb: 0, 0, 0;
29
+
30
+ --accent-primary-subtle: rgba(var(--accent-primary-rgb), 0.06);
31
+ --accent-primary-border: rgba(var(--accent-primary-rgb), 0.12);
32
+ --accent-primary-glow: rgba(var(--accent-primary-rgb), 0.2);
33
+ --accent-primary-ring: rgba(var(--accent-primary-rgb), 0.15);
34
+ --accent-secondary-subtle: rgba(var(--accent-secondary-rgb), 0.06);
35
+ --accent-secondary-border: rgba(var(--accent-secondary-rgb), 0.12);
36
+ --accent-secondary-glow: rgba(var(--accent-secondary-rgb), 0.2);
37
+
38
+ --color-success: rgb(52, 211, 153);
39
+ --color-success-rgb: 52, 211, 153;
40
+ --color-error: rgb(239, 68, 68);
41
+ --color-error-subtle: rgba(239, 68, 68, 0.1);
42
+ --color-warning: rgb(245, 158, 11);
43
+ --color-warning-rgb: 245, 158, 11;
44
+ --color-warning-subtle: rgba(245, 158, 11, 0.1);
45
+ --color-info: rgb(59, 130, 246);
46
+ --color-info-rgb: 59, 130, 246;
47
+ --color-info-subtle: rgba(59, 130, 246, 0.1);
48
+
49
+ --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.2);
50
+ --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.2), 0 1px 2px rgba(0, 0, 0, 0.15);
51
+ --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.25), 0 2px 4px rgba(0, 0, 0, 0.15);
52
+ --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.3), 0 4px 8px rgba(0, 0, 0, 0.15);
53
+ --shadow-xl: 0 16px 48px rgba(0, 0, 0, 0.35), 0 8px 16px rgba(0, 0, 0, 0.15);
54
+ --shadow-overlay: 0 8px 32px rgba(0, 0, 0, 0.4);
55
+
56
+ --z-base: 0;
57
+ --z-dropdown: 100;
58
+ --z-sticky: 200;
59
+ --z-drawer: 300;
60
+ --z-modal: 400;
61
+ --z-popover: 500;
62
+ --z-toast: 600;
63
+ --z-tooltip: 700;
64
+ --z-max: 9999;
65
+
66
+ --breakpoint-xs: 480px;
67
+ --breakpoint-sm: 640px;
68
+ --breakpoint-md: 768px;
69
+ --breakpoint-lg: 1024px;
70
+ --breakpoint-xl: 1280px;
71
+ --breakpoint-2xl: 1536px;
72
+
73
+ --opacity-disabled: 0.5;
74
+ --opacity-muted: 0.6;
75
+ --opacity-hover: 0.8;
76
+ --opacity-visible: 1;
77
+
78
+ --font-body: 'Host Grotesk', system-ui, sans-serif;
79
+ --font-accent: 'Tektur', system-ui, sans-serif;
80
+ --font-mono: 'JetBrains Mono', ui-monospace, monospace;
81
+
82
+ --text-xs: 12px;
83
+ --text-sm: 14px;
84
+ --text-md: 15px;
85
+ --text-lg: clamp(18px, 1.5vw, 20px);
86
+ --text-xl: clamp(22px, 2.5vw, 26px);
87
+ --text-2xl: clamp(28px, 3vw, 36px);
88
+ --text-3xl: clamp(36px, 5vw, 52px);
89
+
90
+ --display-xl-size: var(--text-3xl);
91
+ --display-xl-weight: 500;
92
+ --display-xl-spacing: -1px;
93
+ --heading-size: var(--text-xl);
94
+ --heading-weight: 500;
95
+ --heading-lh: 1.2;
96
+ --body-size: var(--text-md);
97
+ --body-weight: 500;
98
+ --body-lh: 1.7;
99
+ --wordmark-size: clamp(20px, 2.5vw, 28px);
100
+ --wordmark-weight: 500;
101
+ --wordmark-spacing: clamp(8px, 1.2vw, 14px);
102
+ --section-title-size: var(--text-xs);
103
+ --section-title-weight: 600;
104
+ --section-title-spacing: 4px;
105
+ --ui-accent-size: 16px;
106
+ --ui-accent-weight: 600;
107
+ --ui-accent-spacing: 1px;
108
+ --code-size: var(--text-sm);
109
+ --code-lh: 1.8;
110
+ --label-inline-size: var(--text-xs);
111
+ --label-inline-spacing: 3px;
112
+
113
+ --touch-min: 24px;
114
+ --touch-pad: 4px;
115
+
116
+ --space-xs: 4px;
117
+ --space-sm: 8px;
118
+ --space-md: 16px;
119
+ --space-lg: 24px;
120
+ --space-xl: 40px;
121
+ --space-2xl: 64px;
122
+ --space-3xl: 96px;
123
+ --space-4xl: 128px;
124
+
125
+ --radius-sm: 4px;
126
+ --radius-md: 10px;
127
+ --radius-lg: 14px;
128
+ --radius-xl: 20px;
129
+ --radius-full: 9999px;
130
+
131
+ --transition-fast: 120ms ease;
132
+ --transition-base: 200ms ease;
133
+ --transition-slow: 400ms ease;
134
+
135
+ --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
136
+ --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
137
+ --duration-enter: 500ms;
138
+ --duration-exit: 300ms;
139
+
140
+ --gradient-display-text: linear-gradient(135deg, rgb(232, 232, 236) 0%, rgb(124, 124, 137) 100%);
141
+ --gradient-accent-text: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
142
+ --gradient-divider: linear-gradient(90deg, transparent, rgb(24, 24, 30), transparent);
143
+ --gradient-divider-glow: linear-gradient(90deg, transparent, rgba(var(--accent-primary-rgb),0.2), rgba(var(--accent-secondary-rgb),0.12), transparent);
144
+ --gradient-page-ambient: radial-gradient(ellipse, rgba(255, 255, 255,0.015) 0%, transparent 70%);
145
+
146
+ --glow-white: 0 0 6px rgba(var(--text-primary-rgb),0.6), 0 0 18px rgba(var(--text-primary-rgb),0.25), 0 0 40px rgba(var(--text-primary-rgb),0.1);
147
+ --glow-primary: 0 0 8px rgba(var(--accent-primary-rgb),0.9), 0 0 20px rgba(var(--accent-primary-rgb),0.5), 0 0 44px rgba(var(--accent-primary-rgb),0.25), 0 0 80px rgba(var(--accent-primary-rgb),0.1);
148
+ --glow-secondary: 0 0 8px rgba(var(--accent-secondary-rgb),0.9), 0 0 20px rgba(var(--accent-secondary-rgb),0.4), 0 0 40px rgba(var(--accent-secondary-rgb),0.15);
149
+
150
+ --glow-line-white: linear-gradient(90deg, transparent, rgba(var(--text-primary-rgb),0.35), transparent);
151
+ --glow-line-blue: linear-gradient(90deg, transparent, rgba(var(--accent-primary-rgb),0.7), transparent);
152
+ --glow-line-gradient: linear-gradient(90deg, transparent, var(--accent-primary), var(--accent-secondary), transparent);
153
+
154
+ --glow-card-hover: 0 0 20px rgba(var(--accent-primary-rgb),0.08), 0 0 40px rgba(var(--accent-secondary-rgb),0.04);
155
+ --gradient-border-glow: linear-gradient(135deg, rgba(var(--accent-primary-rgb),0.15), rgba(var(--accent-secondary-rgb),0.1), rgba(var(--accent-primary-rgb),0.05));
156
+ --gradient-ambient: radial-gradient(circle at 15% 85%, rgba(var(--accent-primary-rgb),0.04) 0%, transparent 50%),
157
+ radial-gradient(circle at 85% 15%, rgba(var(--accent-secondary-rgb),0.03) 0%, transparent 50%);
158
+
159
+ --focus-ring: 0 0 0 1px rgba(var(--accent-primary-rgb),0.25);
160
+ --focus-glow: 0 0 0 1px rgba(var(--accent-primary-rgb),0.2), 0 0 6px rgba(var(--accent-primary-rgb),0.35), 0 0 16px rgba(var(--accent-primary-rgb),0.2), 0 0 40px rgba(var(--accent-secondary-rgb),0.12);
161
+
162
+ --max-width: 1120px;
163
+ --max-width-sm: 720px;
164
+ --nav-height: 64px;
165
+
166
+ --bg-hover: rgba(255, 255, 255, 0.04);
167
+ --overlay-backdrop: rgba(0, 0, 0, 0.6);
168
+
169
+ }
170
+
171
+ @media (pointer: coarse) {
172
+ :root {
173
+ --touch-min: 36px;
174
+ --touch-pad: 8px;
175
+ }
176
+ }
177
+
178
+ /* Light Theme Overrides */
179
+ [data-theme="light"] {
180
+ --bg-deep: rgb(242, 242, 248);
181
+ --bg-surface: rgb(248, 248, 252);
182
+ --bg-base: rgb(244, 244, 250);
183
+ --bg-card: rgb(248, 248, 252);
184
+ --bg-elevated: rgb(238, 238, 246);
185
+ --text-primary: rgb(35, 35, 55);
186
+ --text-secondary: rgb(85, 88, 108);
187
+ --text-muted: rgb(115, 118, 140);
188
+ --text-ghost: rgb(150, 152, 172);
189
+ --border-subtle: rgb(230, 232, 238);
190
+ --border-default: rgb(210, 214, 222);
191
+ --border-bright: rgb(190, 195, 205);
192
+ --accent-primary: rgb(55, 105, 235);
193
+ --accent-secondary: rgb(120, 70, 230);
194
+ --accent-primary-rgb: 55, 105, 235;
195
+ --accent-secondary-rgb: 120, 70, 230;
196
+ --text-primary-rgb: 35, 35, 55;
197
+ --text-muted-rgb: 115, 118, 140;
198
+ --shadow-xs: 0 1px 2px rgba(var(--accent-primary-rgb),0.06);
199
+ --shadow-sm: 0 2px 4px rgba(var(--accent-primary-rgb),0.08), 0 1px 2px rgba(var(--accent-secondary-rgb),0.04);
200
+ --shadow-md: 0 4px 12px rgba(var(--accent-primary-rgb),0.1), 0 2px 4px rgba(var(--accent-secondary-rgb),0.05);
201
+ --shadow-lg: 0 8px 24px rgba(var(--accent-primary-rgb),0.12), 0 4px 8px rgba(var(--accent-secondary-rgb),0.06);
202
+ --shadow-xl: 0 16px 48px rgba(var(--accent-primary-rgb),0.14), 0 8px 16px rgba(var(--accent-secondary-rgb),0.07);
203
+ --shadow-overlay: 0 4px 24px rgba(var(--accent-primary-rgb),0.1), 0 8px 48px rgba(var(--accent-secondary-rgb),0.06);
204
+ --gradient-display-text: linear-gradient(135deg, var(--text-primary) 0%, var(--accent-primary) 100%);
205
+ --gradient-divider: linear-gradient(90deg, transparent, rgba(var(--accent-primary-rgb),0.2), transparent);
206
+ --gradient-divider-glow: linear-gradient(90deg, transparent, rgba(var(--accent-primary-rgb),0.35), rgba(var(--accent-secondary-rgb),0.25), transparent);
207
+ --gradient-page-ambient: radial-gradient(ellipse, rgba(var(--accent-primary-rgb),0.06) 0%, transparent 70%);
208
+ --gradient-border-glow: linear-gradient(135deg, rgba(var(--accent-primary-rgb),0.2), rgba(var(--accent-secondary-rgb),0.15), rgba(var(--accent-primary-rgb),0.08));
209
+ --gradient-ambient: radial-gradient(circle at 15% 85%, rgba(var(--accent-primary-rgb),0.07) 0%, transparent 50%),
210
+ radial-gradient(circle at 85% 15%, rgba(var(--accent-secondary-rgb),0.05) 0%, transparent 50%);
211
+ --glow-primary: 0 0 8px rgba(var(--accent-primary-rgb),0.5), 0 0 24px rgba(var(--accent-primary-rgb),0.2), 0 0 48px rgba(var(--accent-primary-rgb),0.08);
212
+ --glow-secondary: 0 0 8px rgba(var(--accent-secondary-rgb),0.5), 0 0 24px rgba(var(--accent-secondary-rgb),0.2), 0 0 48px rgba(var(--accent-secondary-rgb),0.08);
213
+ --glow-white: 0 0 8px rgba(var(--accent-primary-rgb),0.15), 0 0 20px rgba(var(--accent-secondary-rgb),0.08);
214
+ --glow-card-hover: 0 0 20px rgba(var(--accent-primary-rgb),0.12), 0 0 40px rgba(var(--accent-secondary-rgb),0.06);
215
+ --glow-line-white: linear-gradient(90deg, transparent, rgba(var(--accent-primary-rgb),0.15), transparent);
216
+ --glow-line-blue: linear-gradient(90deg, transparent, rgba(var(--accent-primary-rgb),0.6), transparent);
217
+ --bg-hover: rgba(55, 105, 235, 0.04);
218
+ --overlay-backdrop: rgba(20, 20, 40, 0.25);
219
+ }
220
+
221
+ @media (prefers-color-scheme: light) {
222
+ [data-theme="auto"] {
223
+ --bg-deep: rgb(242, 242, 248);
224
+ --bg-surface: rgb(248, 248, 252);
225
+ --bg-base: rgb(244, 244, 250);
226
+ --bg-card: rgb(248, 248, 252);
227
+ --bg-elevated: rgb(238, 238, 246);
228
+ --text-primary: rgb(35, 35, 55);
229
+ --text-secondary: rgb(85, 88, 108);
230
+ --text-muted: rgb(115, 118, 140);
231
+ --text-ghost: rgb(150, 152, 172);
232
+ --border-subtle: rgb(230, 232, 238);
233
+ --border-default: rgb(210, 214, 222);
234
+ --border-bright: rgb(190, 195, 205);
235
+ --accent-primary: rgb(55, 105, 235);
236
+ --accent-secondary: rgb(120, 70, 230);
237
+ --accent-primary-rgb: 55, 105, 235;
238
+ --accent-secondary-rgb: 120, 70, 230;
239
+ --text-primary-rgb: 35, 35, 55;
240
+ --text-muted-rgb: 115, 118, 140;
241
+ --shadow-xs: 0 1px 2px rgba(var(--accent-primary-rgb),0.06);
242
+ --shadow-sm: 0 2px 4px rgba(var(--accent-primary-rgb),0.08), 0 1px 2px rgba(var(--accent-secondary-rgb),0.04);
243
+ --shadow-md: 0 4px 12px rgba(var(--accent-primary-rgb),0.1), 0 2px 4px rgba(var(--accent-secondary-rgb),0.05);
244
+ --shadow-lg: 0 8px 24px rgba(var(--accent-primary-rgb),0.12), 0 4px 8px rgba(var(--accent-secondary-rgb),0.06);
245
+ --shadow-xl: 0 16px 48px rgba(var(--accent-primary-rgb),0.14), 0 8px 16px rgba(var(--accent-secondary-rgb),0.07);
246
+ --shadow-overlay: 0 4px 24px rgba(var(--accent-primary-rgb),0.1), 0 8px 48px rgba(var(--accent-secondary-rgb),0.06);
247
+ --gradient-display-text: linear-gradient(135deg, var(--text-primary) 0%, var(--accent-primary) 100%);
248
+ --gradient-divider: linear-gradient(90deg, transparent, rgba(var(--accent-primary-rgb),0.2), transparent);
249
+ --gradient-divider-glow: linear-gradient(90deg, transparent, rgba(var(--accent-primary-rgb),0.35), rgba(var(--accent-secondary-rgb),0.25), transparent);
250
+ --gradient-page-ambient: radial-gradient(ellipse, rgba(var(--accent-primary-rgb),0.06) 0%, transparent 70%);
251
+ --gradient-border-glow: linear-gradient(135deg, rgba(var(--accent-primary-rgb),0.2), rgba(var(--accent-secondary-rgb),0.15), rgba(var(--accent-primary-rgb),0.08));
252
+ --gradient-ambient: radial-gradient(circle at 15% 85%, rgba(var(--accent-primary-rgb),0.07) 0%, transparent 50%),
253
+ radial-gradient(circle at 85% 15%, rgba(var(--accent-secondary-rgb),0.05) 0%, transparent 50%);
254
+ --glow-primary: 0 0 8px rgba(var(--accent-primary-rgb),0.5), 0 0 24px rgba(var(--accent-primary-rgb),0.2), 0 0 48px rgba(var(--accent-primary-rgb),0.08);
255
+ --glow-secondary: 0 0 8px rgba(var(--accent-secondary-rgb),0.5), 0 0 24px rgba(var(--accent-secondary-rgb),0.2), 0 0 48px rgba(var(--accent-secondary-rgb),0.08);
256
+ --glow-white: 0 0 8px rgba(var(--accent-primary-rgb),0.15), 0 0 20px rgba(var(--accent-secondary-rgb),0.08);
257
+ --glow-card-hover: 0 0 20px rgba(var(--accent-primary-rgb),0.12), 0 0 40px rgba(var(--accent-secondary-rgb),0.06);
258
+ --glow-line-white: linear-gradient(90deg, transparent, rgba(var(--accent-primary-rgb),0.15), transparent);
259
+ --glow-line-blue: linear-gradient(90deg, transparent, rgba(var(--accent-primary-rgb),0.6), transparent);
260
+ --bg-hover: rgba(55, 105, 235, 0.04);
261
+ --overlay-backdrop: rgba(20, 20, 40, 0.25);
262
+ }
263
+ }
264
+
265
+ /* Fixed Dark — always-dark regions (nav, footer) */
266
+ .theme-fixed {
267
+ --bg-deep: rgb(3, 3, 7);
268
+ --bg-surface: rgb(10, 10, 15);
269
+ --bg-base: rgb(10, 10, 15);
270
+ --bg-card: rgb(13, 13, 18);
271
+ --bg-elevated: rgb(17, 17, 22);
272
+ --text-primary: rgb(232, 232, 236);
273
+ --text-secondary: rgb(138, 138, 150);
274
+ --text-muted: rgb(124, 124, 137);
275
+ --text-ghost: rgb(107, 107, 128);
276
+ --border-subtle: rgb(24, 24, 30);
277
+ --border-default: rgb(34, 34, 41);
278
+ --border-bright: rgb(51, 51, 64);
279
+ --accent-primary: rgb(77, 126, 247);
280
+ --accent-secondary: rgb(139, 92, 246);
281
+ --accent-primary-rgb: 77, 126, 247;
282
+ --accent-secondary-rgb: 139, 92, 246;
283
+ --text-primary-rgb: 232, 232, 236;
284
+ --text-muted-rgb: 124, 124, 137;
285
+ --white-rgb: 255, 255, 255;
286
+ --black-rgb: 0, 0, 0;
287
+ --shadow-overlay: 0 8px 32px rgba(0, 0, 0, 0.4);
288
+ --gradient-display-text: linear-gradient(135deg, rgb(232, 232, 236) 0%, rgb(124, 124, 137) 100%);
289
+ --gradient-divider: linear-gradient(90deg, transparent, var(--border-subtle), transparent);
290
+ --bg-hover: rgba(var(--white-rgb), 0.04);
291
+ --overlay-backdrop: rgba(var(--black-rgb), 0.6);
292
+ }
293
+
294
+ [data-theme="light"] .theme-fixed {
295
+ --bg-deep: rgb(12, 12, 52);
296
+ --bg-surface: rgb(16, 16, 62);
297
+ --bg-base: rgb(16, 16, 62);
298
+ --bg-card: rgb(20, 20, 70);
299
+ --bg-elevated: rgb(26, 26, 80);
300
+ --text-secondary: rgb(160, 165, 195);
301
+ --text-muted: rgb(135, 140, 175);
302
+ --text-ghost: rgb(110, 115, 155);
303
+ --border-subtle: rgb(30, 30, 78);
304
+ --border-default: rgb(42, 42, 92);
305
+ --border-bright: rgb(56, 56, 108);
306
+ }
307
+
308
+ @media (prefers-color-scheme: light) {
309
+ [data-theme="auto"] .theme-fixed {
310
+ --bg-deep: rgb(12, 12, 52);
311
+ --bg-surface: rgb(16, 16, 62);
312
+ --bg-base: rgb(16, 16, 62);
313
+ --bg-card: rgb(20, 20, 70);
314
+ --bg-elevated: rgb(26, 26, 80);
315
+ --text-secondary: rgb(160, 165, 195);
316
+ --text-muted: rgb(135, 140, 175);
317
+ --text-ghost: rgb(110, 115, 155);
318
+ --border-subtle: rgb(30, 30, 78);
319
+ --border-default: rgb(42, 42, 92);
320
+ --border-bright: rgb(56, 56, 108);
321
+ }
322
+ }