@gradeui/ui 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/components/ui/button.md +11 -7
  2. package/components/ui/dropdown-menu.md +1 -0
  3. package/components/ui/logo.md +8 -6
  4. package/components/ui/map.md +3 -0
  5. package/components/ui/media-surface.md +1 -0
  6. package/components/ui/sidebar.md +2 -1
  7. package/components/ui/swatch.md +88 -0
  8. package/dist/contracts.js +6 -6
  9. package/dist/contracts.js.map +1 -1
  10. package/dist/contracts.mjs +6 -6
  11. package/dist/contracts.mjs.map +1 -1
  12. package/dist/index.d.mts +583 -393
  13. package/dist/index.d.ts +583 -393
  14. package/dist/index.js +602 -65
  15. package/dist/index.js.map +1 -1
  16. package/dist/index.mjs +602 -65
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/map/google.d.mts +1 -1
  19. package/dist/map/google.d.ts +1 -1
  20. package/dist/map/google.js +1 -1
  21. package/dist/map/google.js.map +1 -1
  22. package/dist/map/google.mjs +1 -1
  23. package/dist/map/google.mjs.map +1 -1
  24. package/dist/map/leaflet.d.mts +1 -1
  25. package/dist/map/leaflet.d.ts +1 -1
  26. package/dist/map/leaflet.js +2 -2
  27. package/dist/map/leaflet.js.map +1 -1
  28. package/dist/map/leaflet.mjs +2 -2
  29. package/dist/map/leaflet.mjs.map +1 -1
  30. package/dist/map/mapbox.d.mts +1 -1
  31. package/dist/map/mapbox.d.ts +1 -1
  32. package/dist/map/mapbox.js +2 -2
  33. package/dist/map/mapbox.js.map +1 -1
  34. package/dist/map/mapbox.mjs +2 -2
  35. package/dist/map/mapbox.mjs.map +1 -1
  36. package/dist/map/maplibre.d.mts +1 -1
  37. package/dist/map/maplibre.d.ts +1 -1
  38. package/dist/map/maplibre.js +1 -1
  39. package/dist/map/maplibre.js.map +1 -1
  40. package/dist/map/maplibre.mjs +1 -1
  41. package/dist/map/maplibre.mjs.map +1 -1
  42. package/dist/styles.css +1 -1
  43. package/dist/{types-BxywIwvG.d.mts → types-B45Uirkp.d.mts} +23 -0
  44. package/dist/{types-BxywIwvG.d.ts → types-B45Uirkp.d.ts} +23 -0
  45. package/package.json +1 -1
  46. package/styles/globals.css +127 -3
@@ -7,6 +7,20 @@ import * as React from 'react';
7
7
  */
8
8
  type Coords = [lng: number, lat: number];
9
9
  type MapAppearance = "light" | "dark" | "satellite" | "auto";
10
+ /**
11
+ * Which built-in map UI (zoom buttons today; compass/scale could join)
12
+ * renders on the map. `"auto"` (default) shows zoom buttons whenever the
13
+ * map is `interactive`; `"zoom"` forces them on regardless; `"none"`
14
+ * hides every tool. Attribution is NOT a tool — it's a license
15
+ * requirement and always renders.
16
+ */
17
+ type MapTools = "auto" | "zoom" | "none";
18
+ /**
19
+ * Corner the tools dock to. One vocabulary across providers — each
20
+ * adapter maps it to its native enum (Leaflet `topleft`, MapLibre/Mapbox
21
+ * `top-left`, Google `ControlPosition.*`).
22
+ */
23
+ type MapToolsPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right";
10
24
  type MapErrorCode = "sdk-missing" | "api-key-missing" | "provider-init-failed" | "style-load-failed" | "tile-load-failed";
11
25
  type MapError = {
12
26
  code: MapErrorCode;
@@ -51,6 +65,13 @@ type MapBaseProps = {
51
65
  appearance?: MapAppearance;
52
66
  /** Default `true`. `false` disables pan/zoom/rotate (static display). */
53
67
  interactive?: boolean;
68
+ /** Which built-in map UI shows. Default `"auto"` — zoom buttons follow
69
+ * `interactive`. `"none"` for chrome-free maps (hero backdrops,
70
+ * posters); attribution always stays (license). */
71
+ tools?: MapTools;
72
+ /** Corner the tools dock to. Default `"top-left"`. Handy when the
73
+ * layout puts a search bar or legend over the default corner. */
74
+ toolsPosition?: MapToolsPosition;
54
75
  /** Controlled hovered marker id — pairs with `onHoveredIdChange` for list↔map sync. */
55
76
  hoveredId?: string | null;
56
77
  onHoveredIdChange?: (id: string | null) => void;
@@ -116,6 +137,8 @@ type AdapterOpts = {
116
137
  bounds?: [Coords, Coords];
117
138
  appearance: "light" | "dark" | "satellite";
118
139
  interactive: boolean;
140
+ tools: MapTools;
141
+ toolsPosition: MapToolsPosition;
119
142
  styleUrl?: string;
120
143
  tilerKey?: string;
121
144
  accessToken?: string;
@@ -7,6 +7,20 @@ import * as React from 'react';
7
7
  */
8
8
  type Coords = [lng: number, lat: number];
9
9
  type MapAppearance = "light" | "dark" | "satellite" | "auto";
10
+ /**
11
+ * Which built-in map UI (zoom buttons today; compass/scale could join)
12
+ * renders on the map. `"auto"` (default) shows zoom buttons whenever the
13
+ * map is `interactive`; `"zoom"` forces them on regardless; `"none"`
14
+ * hides every tool. Attribution is NOT a tool — it's a license
15
+ * requirement and always renders.
16
+ */
17
+ type MapTools = "auto" | "zoom" | "none";
18
+ /**
19
+ * Corner the tools dock to. One vocabulary across providers — each
20
+ * adapter maps it to its native enum (Leaflet `topleft`, MapLibre/Mapbox
21
+ * `top-left`, Google `ControlPosition.*`).
22
+ */
23
+ type MapToolsPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right";
10
24
  type MapErrorCode = "sdk-missing" | "api-key-missing" | "provider-init-failed" | "style-load-failed" | "tile-load-failed";
11
25
  type MapError = {
12
26
  code: MapErrorCode;
@@ -51,6 +65,13 @@ type MapBaseProps = {
51
65
  appearance?: MapAppearance;
52
66
  /** Default `true`. `false` disables pan/zoom/rotate (static display). */
53
67
  interactive?: boolean;
68
+ /** Which built-in map UI shows. Default `"auto"` — zoom buttons follow
69
+ * `interactive`. `"none"` for chrome-free maps (hero backdrops,
70
+ * posters); attribution always stays (license). */
71
+ tools?: MapTools;
72
+ /** Corner the tools dock to. Default `"top-left"`. Handy when the
73
+ * layout puts a search bar or legend over the default corner. */
74
+ toolsPosition?: MapToolsPosition;
54
75
  /** Controlled hovered marker id — pairs with `onHoveredIdChange` for list↔map sync. */
55
76
  hoveredId?: string | null;
56
77
  onHoveredIdChange?: (id: string | null) => void;
@@ -116,6 +137,8 @@ type AdapterOpts = {
116
137
  bounds?: [Coords, Coords];
117
138
  appearance: "light" | "dark" | "satellite";
118
139
  interactive: boolean;
140
+ tools: MapTools;
141
+ toolsPosition: MapToolsPosition;
119
142
  styleUrl?: string;
120
143
  tilerKey?: string;
121
144
  accessToken?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradeui/ui",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Grade Design System — React components, theme engine, and design tokens",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -830,6 +830,24 @@
830
830
  --gds-media-placeholder-bg: oklch(var(--muted));
831
831
  --gds-media-placeholder-fg: oklch(var(--muted-foreground));
832
832
 
833
+ /* Fidelity fade — how long a MediaSurface takes to cross-fade between
834
+ full imagery and the wireframe placeholder when `data-fidelity`
835
+ flips on an ancestor (Studio's toggle, an embed's `fidelity`
836
+ parameter). One knob, read by the "MediaSurface fidelity" rules
837
+ further down. */
838
+ --gds-media-fidelity-fade: 280ms;
839
+
840
+ /* Transparency checkerboard — the standard alpha backdrop drawn behind
841
+ image previews (Studio's inspector image well; anywhere else that
842
+ needs to say "these pixels are transparent"). Consumed as
843
+ `repeating-conic-gradient(var(--gds-media-checker-color) 0% 25%,
844
+ transparent 0% 50%)` sized by the size token. Mode-aware for free:
845
+ the colour rides on --muted-foreground at low alpha, so it reads as
846
+ grey/white in light mode and grey/dark in dark without a `.dark`
847
+ override. */
848
+ --gds-media-checker-color: oklch(var(--muted-foreground) / 0.16);
849
+ --gds-media-checker-size: 14px;
850
+
833
851
  /* Canvas fill — the standard backdrop behind a screen when it doesn't fill
834
852
  its frame: the letterbox bars in an embed/share, and the stage a
835
853
  `<ScreenAnimator>` reveals when it flies in or pulls below 1× zoom. One
@@ -840,6 +858,18 @@
840
858
  mode. Set it to `transparent` to let the host page show through. */
841
859
  --gds-canvas-fill: #e8e8ec;
842
860
 
861
+ /* Map marker chrome — the baseline "lift" every MapMarker child gets
862
+ so pin labels never melt into the tiles (see the map-marker rule
863
+ near the fidelity block). Map tiles are EXTERNAL imagery — they
864
+ don't ride the theme's surface tokens — so marker content needs
865
+ its own guaranteed separation in both modes: a 1px border + a soft
866
+ ambient shadow. Light mode: a slightly-darker-than---border line
867
+ reads against pale tiles. Dark mode override below flips to a
868
+ light hairline (a dark border on dark tiles lifts nothing). */
869
+ --gds-map-marker-border: oklch(var(--border));
870
+ --gds-map-marker-shadow: 0 1px 3px oklch(0 0 0 / 0.22),
871
+ 0 0 0 1px oklch(0 0 0 / 0.05);
872
+
843
873
  /* Sidebar knobs — the width pair drives the collapsed/expanded animation
844
874
  on `<Sidebar>` (compound nav primitive). Header height + section
845
875
  padding + per-section gap are exposed so consumers can retune density
@@ -971,6 +1001,14 @@
971
1001
  bars in embed/share/preview and the ScreenAnimator stage all match. */
972
1002
  --gds-canvas-fill: #0b0b0e;
973
1003
 
1004
+ /* Map marker chrome — dark mode: a light hairline + deeper shadow.
1005
+ Dark-mode pin surfaces (bg-card etc.) sit on dark tiles, so the
1006
+ border has to be the bright element; the alpha keeps it a lift,
1007
+ not a frame. */
1008
+ --gds-map-marker-border: oklch(1 0 0 / 0.3);
1009
+ --gds-map-marker-shadow: 0 1px 3px oklch(0 0 0 / 0.5),
1010
+ 0 0 0 1px oklch(0 0 0 / 0.4);
1011
+
974
1012
  /* Alert surface pairs — dark-mode variant: tint sits just above the dark
975
1013
  background without going gray; deep text stays bright and readable. */
976
1014
  --destructive-soft: 0.220 0.075 27;
@@ -1091,6 +1129,10 @@
1091
1129
  font-feature-settings: "rlig" 1, "calt" 1;
1092
1130
  font-size: var(--text-body);
1093
1131
  line-height: var(--text-body-line);
1132
+ /* Theme-driven width cut (variable-font wdth axis). Inherits into
1133
+ every span/component, so a theme set to e.g. 90% re-cuts ALL text;
1134
+ per-element font-stretch-[…] utilities still override. */
1135
+ font-stretch: var(--font-body-stretch, normal);
1094
1136
  }
1095
1137
 
1096
1138
  /* Theme-driven heading font.
@@ -1102,6 +1144,7 @@
1102
1144
  font-family: var(--font-display, var(--font-sans));
1103
1145
  font-weight: var(--font-heading-weight, 600);
1104
1146
  letter-spacing: var(--font-heading-tracking, -0.01em);
1147
+ font-stretch: var(--font-display-stretch, var(--font-body-stretch, normal));
1105
1148
  }
1106
1149
 
1107
1150
  /* Link styles */
@@ -1221,9 +1264,6 @@
1221
1264
  style overrides like `style={{ "--btn-glow": "var(--warning)" }}`. */
1222
1265
  --btn-glow: var(--accent-glow, var(--selected-glow));
1223
1266
 
1224
- background-color: oklch(var(--secondary));
1225
- color: oklch(var(--secondary-foreground));
1226
-
1227
1267
  box-shadow: var(--elevation-3);
1228
1268
 
1229
1269
  transition:
@@ -1232,6 +1272,17 @@
1232
1272
  background-color var(--gds-transition-base) var(--gds-ease-out);
1233
1273
  }
1234
1274
 
1275
+ /* Surface for the STANDALONE raised look. June 2026: raised became a
1276
+ TRAIT (the `raised` prop on Button adds .gds-button-raised only, so
1277
+ elevation + glow compose with ANY colour variant — raised primary,
1278
+ raised outline). This companion class supplies the classic neutral
1279
+ "key" surface and is applied only by the variant="raised" alias,
1280
+ which is kept for back-compat. */
1281
+ .gds-button-raised-surface {
1282
+ background-color: oklch(var(--secondary));
1283
+ color: oklch(var(--secondary-foreground));
1284
+ }
1285
+
1235
1286
  .gds-button-raised:hover {
1236
1287
  box-shadow: var(--elevation-hot);
1237
1288
  }
@@ -1578,6 +1629,79 @@
1578
1629
  0 0 var(--aura-ring-blur) 0 oklch(var(--aura-color) / calc(var(--aura-ring-alpha-max) * 0.4));
1579
1630
  }
1580
1631
 
1632
+ /* ============================================
1633
+ MEDIASURFACE FIDELITY — data-fidelity on any ancestor
1634
+ ============================================
1635
+ The pure-CSS half of MediaSurface's filled/wireframe model. The
1636
+ component keeps its tiered placeholder MOUNTED beneath a filled
1637
+ image and stamps `data-filled` on it; these rules decide what's
1638
+ visible. Flipping `data-fidelity="wireframe"` on ANY ancestor
1639
+ (Studio's iframe root, an embed wrapper, a docs demo) cross-fades
1640
+ imagery out and the placeholder back in — no React, so the toggle
1641
+ works in static embeds and respects the data-motion="off" reset
1642
+ above (transition collapses to 0.01ms).
1643
+
1644
+ Default (full fidelity): a filled slot hides its placeholder with
1645
+ `visibility: hidden` — NOT unmount — so transparent imagery (logos
1646
+ on alpha) never shows the glyph/`--gds-media-placeholder-bg`
1647
+ through its pixels, while the layer stays in the DOM for this very
1648
+ toggle. The `visibility` transition is delayed by the fade length
1649
+ (the standard fade-then-hide pattern) so the placeholder finishes
1650
+ fading before it stops hit-testing/painting. */
1651
+ [data-gds-part="media-surface-placeholder"][data-filled] {
1652
+ opacity: 0;
1653
+ visibility: hidden;
1654
+ transition:
1655
+ opacity var(--gds-media-fidelity-fade, 280ms) ease,
1656
+ visibility 0s linear var(--gds-media-fidelity-fade, 280ms);
1657
+ }
1658
+ [data-gds-part="media-surface-content"] {
1659
+ transition: opacity var(--gds-media-fidelity-fade, 280ms) ease;
1660
+ }
1661
+
1662
+ /* Wireframe: imagery fades out, placeholder fades back in. Content is
1663
+ opacity-faded (not display-toggled) so the swap reads as a cross-fade;
1664
+ pointer-events off so the invisible imagery can't intercept clicks. */
1665
+ [data-fidelity="wireframe"] [data-gds-part="media-surface-placeholder"][data-filled] {
1666
+ opacity: 1;
1667
+ visibility: visible;
1668
+ transition-delay: 0s, 0s;
1669
+ }
1670
+ [data-fidelity="wireframe"] [data-gds-part="media-surface-content"] {
1671
+ opacity: 0;
1672
+ pointer-events: none;
1673
+ }
1674
+
1675
+ /* ============================================
1676
+ MAP MARKER LIFT — every pin gets a border
1677
+ ============================================
1678
+ Map tiles are external imagery and don't follow the theme, so marker
1679
+ content (Badge price pins, numbered markers, avatars) can melt into
1680
+ them — illegible dark-on-dark pins were the symptom. Guarantee the
1681
+ lift at the DS level: every DIRECT child of a MapMarker's content
1682
+ wrapper gets a 1px border + ambient shadow from the mode-aware
1683
+ `--gds-map-marker-*` pair (light hairline on dark tiles, dark line
1684
+ on light). Being unlayered, this wins over a Badge's own
1685
+ border-transparent utility — deliberate: the border is the floor,
1686
+ not a suggestion. Backgrounds/text are NOT touched; content keeps
1687
+ its own surface tokens. */
1688
+ [data-gds-part="map-marker-content"] > * {
1689
+ border: 1px solid var(--gds-map-marker-border);
1690
+ box-shadow: var(--gds-map-marker-shadow);
1691
+ }
1692
+
1693
+ /* Vendor font reset — Leaflet (`.leaflet-container`) and MapLibre
1694
+ (`.maplibregl-map`) both hard-set `font-family` on their map
1695
+ containers, which beats inheritance and strands marker content on
1696
+ the vendor stack ("pins don't carry the custom font" — they can,
1697
+ the vendor CSS was just in the way). Re-assert the theme stack at
1698
+ the marker-content boundary so Badges/Cards inside pins type-match
1699
+ the rest of the screen, including custom uploaded faces riding
1700
+ `--font-sans`. */
1701
+ [data-gds-part="map-marker-content"] {
1702
+ font-family: var(--font-sans);
1703
+ }
1704
+
1581
1705
  /* ============================================
1582
1706
  STREAM-IN — data-gds-streaming on <html>
1583
1707
  ============================================