@gradeui/ui 3.2.0 → 3.3.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": "@gradeui/ui",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "Grade Design System — React components, theme engine, and design tokens",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -773,6 +773,8 @@
773
773
  ---------------------------------------- */
774
774
  --background: 0.985 0.0018 85; /* neutral 50 */
775
775
  --foreground: 0.170 0.0048 85; /* neutral 950 */
776
+ --bg-base: 0.985 0.0018 85; /* stable mirror for scope-inverse */
777
+ --fg-base: 0.170 0.0048 85;
776
778
  --card: 1 0 0; /* pure white */
777
779
  --card-foreground: 0.170 0.0048 85;
778
780
  --popover: 1 0 0;
@@ -1008,6 +1010,8 @@
1008
1010
  ---------------------------------------- */
1009
1011
  --background: 0.170 0.0048 85;
1010
1012
  --foreground: 0.985 0.0018 85;
1013
+ --bg-base: 0.170 0.0048 85;
1014
+ --fg-base: 0.985 0.0018 85;
1011
1015
  --card: 0.245 0.0096 85; /* neutral 900 */
1012
1016
  --card-foreground: 0.985 0.0018 85;
1013
1017
  --popover: 0.245 0.0096 85;
@@ -1123,6 +1127,8 @@
1123
1127
  :root[data-grade-theme="energy"] {
1124
1128
  --background: 0.985 0.0012 175; /* neutral 50 */
1125
1129
  --foreground: 0.170 0.0032 175; /* neutral 950 */
1130
+ --bg-base: 0.985 0.0012 175;
1131
+ --fg-base: 0.170 0.0032 175;
1126
1132
  --card: 1 0 0;
1127
1133
  --card-foreground: 0.170 0.0032 175;
1128
1134
  --popover: 1 0 0;
@@ -1147,6 +1153,8 @@
1147
1153
  .dark[data-grade-theme="energy"] {
1148
1154
  --background: 0.170 0.0032 175;
1149
1155
  --foreground: 0.985 0.0012 175;
1156
+ --bg-base: 0.170 0.0032 175;
1157
+ --fg-base: 0.985 0.0012 175;
1150
1158
  --card: 0.245 0.0064 175;
1151
1159
  --card-foreground: 0.985 0.0012 175;
1152
1160
  --popover: 0.245 0.0064 175;
@@ -1725,22 +1733,15 @@
1725
1733
  }
1726
1734
 
1727
1735
  /* ============================================
1728
- MAP MARKER LIFT every pin gets a border
1736
+ MAP MARKER — intentionally unstyled
1729
1737
  ============================================
1730
- Map tiles are external imagery and don't follow the theme, so marker
1731
- content (Badge price pins, numbered markers, avatars) can melt into
1732
- them illegible dark-on-dark pins were the symptom. Guarantee the
1733
- lift at the DS level: every DIRECT child of a MapMarker's content
1734
- wrapper gets a 1px border + ambient shadow from the mode-aware
1735
- `--gds-map-marker-*` pair (light hairline on dark tiles, dark line
1736
- on light). Being unlayered, this wins over a Badge's own
1737
- border-transparent utility — deliberate: the border is the floor,
1738
- not a suggestion. Backgrounds/text are NOT touched; content keeps
1739
- its own surface tokens. */
1740
- [data-gds-part="map-marker-content"] > * {
1741
- border: 1px solid var(--gds-map-marker-border);
1742
- box-shadow: var(--gds-map-marker-shadow);
1743
- }
1738
+ Marker content (Badge price pins, numbered markers, avatars, custom pin
1739
+ types) is the CONSUMER's design. The DS used to force a 1px border + ambient
1740
+ shadow on every direct child of a marker as a legibility "floor", but that's
1741
+ too opinionated for a primitive it imposed a look on content you supply.
1742
+ Removed. The mode-aware `--gds-map-marker-border` / `--gds-map-marker-shadow`
1743
+ tokens remain defined, and `.gds-map-label` (below) still gives text a halo,
1744
+ so legibility on busy tiles is OPT-IN, not mandatory. */
1744
1745
 
1745
1746
  /* Floating map label halo — put this class on a text element inside a
1746
1747
  MapMarker to give it a mode-aware outline (white on light tiles,
@@ -2072,6 +2073,153 @@ html[data-gds-streaming] body * {
2072
2073
  }
2073
2074
  }
2074
2075
 
2076
+ @layer utilities {
2077
+ /* Colour SCOPES (STUDIO-COLOR.md) — a local colour mode, the same idea as
2078
+ light/dark but scoped to a wrapper and focused on surfaces.
2079
+ Apply `scope-inverse` (etc.) to any element and it becomes a mini themed
2080
+ region: it re-points the SURFACE family of tokens — page background, card,
2081
+ popover, muted, border, and every foreground — at the named pair, so the
2082
+ whole subtree re-tones (a dark band with light text, cards that sit on the
2083
+ band instead of glaring white). Descendants keep using the ordinary tokens
2084
+ (bg-background / text-foreground / bg-card); only what they resolve to
2085
+ changes. It deliberately leaves the ACTION colours (--primary / --accent /
2086
+ --secondary / --destructive) untouched, so a CTA stays branded and vivid
2087
+ on top of the band.
2088
+ `inverse` reads --fg-base / --bg-base (stable literal mirrors of the page
2089
+ ink/paper that the generator emits) so the fg/bg swap can't form a custom-
2090
+ property cycle; the other scopes read the theme's own tokens directly. */
2091
+ .scope-default,
2092
+ .scope-inverse,
2093
+ .scope-brand,
2094
+ .scope-accent,
2095
+ .scope-muted,
2096
+ .scope-card {
2097
+ background-color: oklch(var(--background));
2098
+ color: oklch(var(--foreground));
2099
+ }
2100
+ /* default = identity: paints the page tokens as-is, no overrides. */
2101
+ .scope-inverse {
2102
+ --background: var(--fg-base);
2103
+ --foreground: var(--bg-base);
2104
+ --card: var(--fg-base);
2105
+ --card-foreground: var(--bg-base);
2106
+ --popover: var(--fg-base);
2107
+ --popover-foreground: var(--bg-base);
2108
+ --muted: var(--fg-base);
2109
+ --muted-foreground: var(--bg-base);
2110
+ --border: var(--bg-base);
2111
+ --input: var(--bg-base);
2112
+ }
2113
+ .scope-brand {
2114
+ --background: var(--primary);
2115
+ --foreground: var(--primary-foreground);
2116
+ --card: var(--primary);
2117
+ --card-foreground: var(--primary-foreground);
2118
+ --popover: var(--primary);
2119
+ --popover-foreground: var(--primary-foreground);
2120
+ --muted: var(--primary);
2121
+ --muted-foreground: var(--primary-foreground);
2122
+ --border: var(--primary-foreground);
2123
+ --input: var(--primary-foreground);
2124
+ }
2125
+ .scope-accent {
2126
+ --background: var(--accent);
2127
+ --foreground: var(--accent-foreground);
2128
+ --card: var(--accent);
2129
+ --card-foreground: var(--accent-foreground);
2130
+ --popover: var(--accent);
2131
+ --popover-foreground: var(--accent-foreground);
2132
+ --muted: var(--accent);
2133
+ --muted-foreground: var(--accent-foreground);
2134
+ --border: var(--accent-foreground);
2135
+ --input: var(--accent-foreground);
2136
+ }
2137
+ .scope-muted {
2138
+ --background: var(--muted);
2139
+ --card: var(--muted);
2140
+ --popover: var(--muted);
2141
+ /* foreground + muted-foreground stay the page's: already tuned to read on
2142
+ a muted surface. */
2143
+ }
2144
+ .scope-card {
2145
+ --background: var(--card);
2146
+ --foreground: var(--card-foreground);
2147
+ --popover: var(--card);
2148
+ --popover-foreground: var(--card-foreground);
2149
+ --muted-foreground: var(--card-foreground);
2150
+ }
2151
+ }
2152
+
2153
+ /* ============================================
2154
+ EXPRESSIVE colour layer — see STUDIO-EXPRESSIVE.md
2155
+ A highlight layer for painting whole SECTIONS, deliberately louder than the
2156
+ neutral product chrome (marketing bands, feature cards, promo strips).
2157
+ Orthogonal to the surface scopes above: it does NOT touch --primary/--accent,
2158
+ so it stacks on top of any theme. Two attributes resolve a section's colour:
2159
+ data-expressive="accent1..5" picks one of 5 accent slots
2160
+ data-expressive-tier="…" picks one of 4 tiers (default: light)
2161
+ …landing an accessible pair on --gds-expressive-bg / --gds-expressive-fg,
2162
+ which the `.expressive` helper paints. Values mirror the Figma "Expressive"
2163
+ collection (accent1..5 = rose, orange, amber, yellow, olive). The accents are
2164
+ fixed ramps today; the generator will later write them from one hue per slot
2165
+ — the token shape stays identical. Tiers use ramp steps 100/300/700/900. */
2166
+ :root {
2167
+ --gds-expressive-accent1-100: #ffe9e6; --gds-expressive-accent1-300: #ffaaa2; --gds-expressive-accent1-700: #8b181d; --gds-expressive-accent1-900: #3f0b0b;
2168
+ --gds-expressive-accent2-100: #ffeae0; --gds-expressive-accent2-300: #ffae89; --gds-expressive-accent2-700: #822c00; --gds-expressive-accent2-900: #3c1100;
2169
+ --gds-expressive-accent3-100: #ffecd8; --gds-expressive-accent3-300: #f6b575; --gds-expressive-accent3-700: #743b00; --gds-expressive-accent3-900: #331900;
2170
+ --gds-expressive-accent4-100: #fdefd2; --gds-expressive-accent4-300: #e5bf6d; --gds-expressive-accent4-700: #634600; --gds-expressive-accent4-900: #2d1d00;
2171
+ --gds-expressive-accent5-100: #f4f2d4; --gds-expressive-accent5-300: #cfc871; --gds-expressive-accent5-700: #544e00; --gds-expressive-accent5-900: #252100;
2172
+ }
2173
+
2174
+ /* accent slot → generic step vars */
2175
+ [data-expressive="accent1"] { --exp-100: var(--gds-expressive-accent1-100); --exp-300: var(--gds-expressive-accent1-300); --exp-700: var(--gds-expressive-accent1-700); --exp-900: var(--gds-expressive-accent1-900); }
2176
+ [data-expressive="accent2"] { --exp-100: var(--gds-expressive-accent2-100); --exp-300: var(--gds-expressive-accent2-300); --exp-700: var(--gds-expressive-accent2-700); --exp-900: var(--gds-expressive-accent2-900); }
2177
+ [data-expressive="accent3"] { --exp-100: var(--gds-expressive-accent3-100); --exp-300: var(--gds-expressive-accent3-300); --exp-700: var(--gds-expressive-accent3-700); --exp-900: var(--gds-expressive-accent3-900); }
2178
+ [data-expressive="accent4"] { --exp-100: var(--gds-expressive-accent4-100); --exp-300: var(--gds-expressive-accent4-300); --exp-700: var(--gds-expressive-accent4-700); --exp-900: var(--gds-expressive-accent4-900); }
2179
+ [data-expressive="accent5"] { --exp-100: var(--gds-expressive-accent5-100); --exp-300: var(--gds-expressive-accent5-300); --exp-700: var(--gds-expressive-accent5-700); --exp-900: var(--gds-expressive-accent5-900); }
2180
+
2181
+ /* tier → which steps become bg / fg (default = light) */
2182
+ [data-expressive] { --gds-expressive-bg: var(--exp-300); --gds-expressive-fg: var(--exp-900); }
2183
+ [data-expressive][data-expressive-tier="superlight"] { --gds-expressive-bg: var(--exp-100); --gds-expressive-fg: var(--exp-900); }
2184
+ [data-expressive][data-expressive-tier="light"] { --gds-expressive-bg: var(--exp-300); --gds-expressive-fg: var(--exp-900); }
2185
+ [data-expressive][data-expressive-tier="dark"] { --gds-expressive-bg: var(--exp-700); --gds-expressive-fg: var(--exp-100); }
2186
+ [data-expressive][data-expressive-tier="superdark"] { --gds-expressive-bg: var(--exp-900); --gds-expressive-fg: var(--exp-100); }
2187
+
2188
+ /* the surface helper a section wears: paints the resolved expressive pair */
2189
+ .expressive { background-color: var(--gds-expressive-bg); color: var(--gds-expressive-fg); }
2190
+
2191
+ /* ============================================
2192
+ REVEAL — scroll-in motion for sections (and any element). Token-driven:
2193
+ reuses the DS transition durations + eases (above), adds a reveal distance.
2194
+ An element with `data-reveal` starts hidden/offset; an IntersectionObserver
2195
+ in the consumer adds `.in-view` to animate it in. Entirely disabled under
2196
+ prefers-reduced-motion (the hidden state lives inside the media query, so
2197
+ content is always visible if motion is off or JS never runs). */
2198
+ :root {
2199
+ --gds-reveal-distance: 1.25rem;
2200
+ --gds-reveal-duration: var(--gds-transition-slower); /* 500ms */
2201
+ --gds-reveal-ease: var(--gds-ease-out);
2202
+ }
2203
+
2204
+ @media (prefers-reduced-motion: no-preference) {
2205
+ [data-reveal] {
2206
+ opacity: 0;
2207
+ transform: translateY(var(--gds-reveal-distance));
2208
+ transition:
2209
+ opacity var(--gds-reveal-duration) var(--gds-reveal-ease),
2210
+ transform var(--gds-reveal-duration) var(--gds-reveal-ease);
2211
+ will-change: opacity, transform;
2212
+ }
2213
+ [data-reveal="fade"] { transform: none; }
2214
+ [data-reveal="fade-down"] { transform: translateY(calc(-1 * var(--gds-reveal-distance))); }
2215
+ [data-reveal="fade-left"] { transform: translateX(var(--gds-reveal-distance)); }
2216
+ [data-reveal="fade-right"] { transform: translateX(calc(-1 * var(--gds-reveal-distance))); }
2217
+ [data-reveal].in-view {
2218
+ opacity: 1;
2219
+ transform: none;
2220
+ }
2221
+ }
2222
+
2075
2223
  /* Keyframes */
2076
2224
  @keyframes fadeIn {
2077
2225
  from { opacity: 0; }