@021.is/brand-studio 0.6.1 → 0.6.7

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.
@@ -20,7 +20,10 @@ type BrandStudioProps = {
20
20
  animatedLogo?: ReactNode;
21
21
  /** Host app's own glyphs for the Icons section. The lib ships none. */
22
22
  icons?: BrandIcon[];
23
+ /** Real wordmark lockups: Overview headline + Download previews. */
24
+ wordmark?: ReactNode;
25
+ wordmarkDark?: ReactNode;
23
26
  };
24
- declare function BrandStudio({ config, route, basePath, staticLogo, staticLogoDark, animatedLogo, icons, }: BrandStudioProps): react_jsx_runtime.JSX.Element;
27
+ declare function BrandStudio({ config, route, basePath, staticLogo, staticLogoDark, animatedLogo, icons, wordmark, wordmarkDark, }: BrandStudioProps): react_jsx_runtime.JSX.Element;
25
28
 
26
29
  export { type BrandIcon as B, BrandStudio as a, type BrandStudioProps as b };
@@ -1,4 +1,4 @@
1
- export { B as BrandIcon, a as BrandStudio, b as BrandStudioProps } from '../BrandStudio-D1QR4hIC.js';
1
+ export { B as BrandIcon, a as BrandStudio, b as BrandStudioProps } from '../BrandStudio-ksHBhiX3.js';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import { ReactNode } from 'react';
4
4
  import { c as BrandStudioSection } from '../types-BycZ2igw.js';
package/dist/app/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- export { BrandStudio, Navigation, buildNavigation } from '../chunk-L4FQAO3K.js';
2
+ export { BrandStudio, Navigation, buildNavigation } from '../chunk-H3LCFXCK.js';
3
3
  import '../chunk-YDZA26YU.js';
4
4
  import '../chunk-MDTU2JR5.js';
5
5
  export { contrastBadge, contrastRatio, relativeLuminance } from '../chunk-JQV3ASME.js';
@@ -10,7 +10,6 @@ var ITEMS = [
10
10
  { section: BrandStudioSection.Colors, label: "Colors" },
11
11
  { section: BrandStudioSection.Typography, label: "Type" },
12
12
  { section: BrandStudioSection.Icons, label: "Icons" },
13
- { section: BrandStudioSection.Motion, label: "Motion" },
14
13
  { section: BrandStudioSection.Download, label: "Download" }
15
14
  ];
16
15
  function buildNavigation(basePath) {
@@ -30,7 +29,7 @@ function Navigation({ current, basePath, renderLink }) {
30
29
  gap: "1.5rem",
31
30
  listStyle: "none",
32
31
  margin: 0,
33
- padding: "0 0 0.75rem 0",
32
+ padding: 0,
34
33
  fontFamily: "var(--bs-mono, ui-monospace, monospace)",
35
34
  fontSize: "0.78rem",
36
35
  letterSpacing: "0.12em",
@@ -182,7 +181,9 @@ var WORDMARK_HEIGHTS = [32, 48, 64, 96, 128];
182
181
  function DownloadSection({
183
182
  config,
184
183
  staticLogo,
185
- staticLogoDark
184
+ staticLogoDark,
185
+ wordmark,
186
+ wordmarkDark
186
187
  }) {
187
188
  const iconLightRef = useRef(null);
188
189
  const iconDarkRef = useRef(null);
@@ -275,7 +276,9 @@ function DownloadSection({
275
276
  style: {
276
277
  display: "grid",
277
278
  gap: "1.5rem",
278
- gridTemplateColumns: "repeat(auto-fit, minmax(20rem, 1fr))"
279
+ // min(20rem, 100%) so the 20rem floor never exceeds the container on
280
+ // narrow viewports (which would force horizontal overflow).
281
+ gridTemplateColumns: "repeat(auto-fit, minmax(min(20rem, 100%), 1fr))"
279
282
  },
280
283
  children: [
281
284
  /* @__PURE__ */ jsxs("div", { style: cardStyle, children: [
@@ -318,8 +321,8 @@ function DownloadSection({
318
321
  /* @__PURE__ */ jsxs("div", { style: cardStyle, children: [
319
322
  /* @__PURE__ */ jsx("p", { style: eyebrowStyle, children: "Wordmark \xB7 logo text" }),
320
323
  /* @__PURE__ */ jsxs("div", { style: previewRow, children: [
321
- /* @__PURE__ */ jsx(PreviewPane, { bg: lightBg, label: "Light", labelColor: lightLabel, children: /* @__PURE__ */ jsx(WordmarkPreview, { name: config.name, sans, color: lightInk }) }),
322
- /* @__PURE__ */ jsx(PreviewPane, { bg: darkBg, label: "Dark", labelColor: darkLabel, children: /* @__PURE__ */ jsx(WordmarkPreview, { name: config.name, sans, color: darkInk }) })
324
+ /* @__PURE__ */ jsx(PreviewPane, { bg: lightBg, label: "Light", labelColor: lightLabel, children: wordmark ?? /* @__PURE__ */ jsx(WordmarkPreview, { name: config.name, sans, color: lightInk }) }),
325
+ /* @__PURE__ */ jsx(PreviewPane, { bg: darkBg, label: "Dark", labelColor: darkLabel, children: wordmarkDark ?? wordmark ?? /* @__PURE__ */ jsx(WordmarkPreview, { name: config.name, sans, color: darkInk }) })
323
326
  ] }),
324
327
  /* @__PURE__ */ jsx(
325
328
  Dropdown,
@@ -450,12 +453,40 @@ function UrlPair({ light, dark }) {
450
453
  function UrlRow({ label, entry }) {
451
454
  const [copied, setCopied] = useState(false);
452
455
  const url = entry?.url ?? null;
453
- function onCopy() {
454
- if (!url || typeof navigator === "undefined" || !navigator.clipboard) return;
455
- navigator.clipboard.writeText(toAbsolute(url)).then(() => {
456
+ async function onCopy() {
457
+ if (!url || typeof window === "undefined") return;
458
+ const text = toAbsolute(url);
459
+ let ok = false;
460
+ if (typeof navigator !== "undefined" && navigator.clipboard) {
461
+ try {
462
+ await navigator.clipboard.writeText(text);
463
+ ok = true;
464
+ } catch {
465
+ ok = false;
466
+ }
467
+ }
468
+ if (!ok) {
469
+ try {
470
+ const ta = document.createElement("textarea");
471
+ ta.value = text;
472
+ ta.setAttribute("readonly", "");
473
+ ta.style.position = "fixed";
474
+ ta.style.top = "0";
475
+ ta.style.left = "0";
476
+ ta.style.opacity = "0";
477
+ document.body.appendChild(ta);
478
+ ta.focus();
479
+ ta.select();
480
+ ok = document.execCommand("copy");
481
+ document.body.removeChild(ta);
482
+ } catch {
483
+ ok = false;
484
+ }
485
+ }
486
+ if (ok) {
456
487
  setCopied(true);
457
- window.setTimeout(() => setCopied(false), 1200);
458
- });
488
+ window.setTimeout(() => setCopied(false), 1600);
489
+ }
459
490
  }
460
491
  const disabled = !url;
461
492
  const downloadName = entry?.filename ?? lastSegment(url ?? "");
@@ -504,12 +535,15 @@ function UrlRow({ label, entry }) {
504
535
  type: "button",
505
536
  onClick: onCopy,
506
537
  disabled,
507
- title: "Copy URL",
538
+ title: copied ? "URL copied" : "Copy URL",
539
+ "aria-live": "polite",
508
540
  style: {
509
541
  ...copyButtonStyle,
542
+ background: copied ? "var(--md-primary)" : copyButtonStyle.background,
543
+ color: copied ? "var(--md-on-primary)" : copyButtonStyle.color,
510
544
  cursor: disabled ? "not-allowed" : "pointer"
511
545
  },
512
- children: copied ? "Copied" : "Copy"
546
+ children: copied ? "\u2713 Copied" : "Copy"
513
547
  }
514
548
  )
515
549
  ]
@@ -648,7 +682,11 @@ var cardStyle = {
648
682
  border: "1px solid var(--md-outline-variant)",
649
683
  borderRadius: "0.85rem",
650
684
  padding: "1.25rem",
651
- background: "var(--md-surface-container-low)"
685
+ background: "var(--md-surface-container-low)",
686
+ // Grid items default to min-width:auto (won't shrink below content min-content),
687
+ // so the URL row's truncating <a> can't shrink and the row overflows the card.
688
+ // minWidth:0 lets the card shrink and the ellipsis do its job.
689
+ minWidth: 0
652
690
  };
653
691
  var eyebrowStyle = {
654
692
  fontFamily: "var(--bs-mono, ui-monospace, monospace)",
@@ -774,7 +812,7 @@ function IconsSection({ staticLogo, icons }) {
774
812
  gap: "1.5rem"
775
813
  },
776
814
  children: [
777
- staticLogo ? /* @__PURE__ */ jsx(Tile, { label: "logo", children: staticLogo }) : null,
815
+ staticLogo && items.length === 0 ? /* @__PURE__ */ jsx(Tile, { label: "logo", children: staticLogo }) : null,
778
816
  items.map((it, i) => /* @__PURE__ */ jsx(Tile, { label: it.label, children: it.node }, it.label ?? `icon-${i}`))
779
817
  ]
780
818
  }
@@ -917,128 +955,13 @@ var eyebrow4 = {
917
955
  color: "var(--md-primary)",
918
956
  margin: 0
919
957
  };
920
- var PRIMITIVES = [
921
- ["Sweep", "Wipe a highlight across a mark"],
922
- ["Drift", "Gentle idle float"],
923
- ["PulseLoop", "Scale + fade breathing"],
924
- ["RingDraw", "Stroke a ring into place"],
925
- ["Wobble", "Playful rotate-settle"],
926
- ["AppearGrow", "Pop in from nothing"],
927
- ["Sequence", "Compose steps, persist + loop"],
928
- ["Strikethrough", "Draw a line through"]
929
- ];
930
- function MotionPlaygroundSection({
931
- animatedLogo
958
+ function OverviewSection({
959
+ config,
960
+ lockup
932
961
  }) {
933
962
  return /* @__PURE__ */ jsxs("section", { children: [
934
- /* @__PURE__ */ jsxs("header", { children: [
935
- /* @__PURE__ */ jsx("p", { style: eyebrow4, children: "Motion" }),
936
- /* @__PURE__ */ jsx(
937
- "h2",
938
- {
939
- style: {
940
- fontSize: "clamp(1.5rem, 3vw, 2.25rem)",
941
- letterSpacing: "-0.02em",
942
- margin: "0.4rem 0 0",
943
- fontWeight: 600,
944
- color: "var(--md-on-surface)"
945
- },
946
- children: "Loading."
947
- }
948
- ),
949
- /* @__PURE__ */ jsxs(
950
- "p",
951
- {
952
- style: { maxWidth: "56ch", color: "var(--md-on-surface-variant)", marginTop: "0.75rem" },
953
- children: [
954
- "The brand mark as a loading state. Pass your own animated mark via",
955
- " ",
956
- /* @__PURE__ */ jsx("code", { style: { fontFamily: "var(--bs-mono)" }, children: "animatedLogo" }),
957
- "; otherwise a neutral spinner stands in."
958
- ]
959
- }
960
- )
961
- ] }),
962
- /* @__PURE__ */ jsx(
963
- "div",
964
- {
965
- style: {
966
- marginTop: "2rem",
967
- display: "flex",
968
- justifyContent: "center",
969
- padding: "3rem",
970
- borderRadius: "0.75rem",
971
- background: "var(--md-surface-container)",
972
- border: "1px solid var(--md-outline-variant)",
973
- color: "var(--md-primary)"
974
- },
975
- children: animatedLogo ?? /* @__PURE__ */ jsx(SpinnerLoadingMark, { size: 220, foreground: "var(--md-primary)" })
976
- }
977
- ),
978
- /* @__PURE__ */ jsxs("div", { style: { marginTop: "2.5rem" }, children: [
979
- /* @__PURE__ */ jsx("p", { style: { ...eyebrow4, fontSize: "0.7rem", marginBottom: "1rem" }, children: "Motion primitives" }),
980
- /* @__PURE__ */ jsx(
981
- "div",
982
- {
983
- style: {
984
- display: "grid",
985
- gap: "0.6rem",
986
- gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))"
987
- },
988
- children: PRIMITIVES.map(([name, desc]) => /* @__PURE__ */ jsxs(
989
- "div",
990
- {
991
- style: {
992
- padding: "0.9rem 1.1rem",
993
- borderRadius: "0.6rem",
994
- border: "1px solid var(--md-outline-variant)",
995
- background: "var(--md-surface-container-low)"
996
- },
997
- children: [
998
- /* @__PURE__ */ jsx(
999
- "p",
1000
- {
1001
- style: {
1002
- margin: 0,
1003
- fontFamily: "var(--bs-mono)",
1004
- fontSize: "0.82rem",
1005
- color: "var(--md-on-surface)"
1006
- },
1007
- children: name
1008
- }
1009
- ),
1010
- /* @__PURE__ */ jsx(
1011
- "p",
1012
- {
1013
- style: {
1014
- margin: "0.3rem 0 0",
1015
- fontSize: "0.8rem",
1016
- color: "var(--md-on-surface-variant)"
1017
- },
1018
- children: desc
1019
- }
1020
- )
1021
- ]
1022
- },
1023
- name
1024
- ))
1025
- }
1026
- )
1027
- ] })
1028
- ] });
1029
- }
1030
- var eyebrow5 = {
1031
- fontFamily: "var(--bs-mono)",
1032
- fontSize: "0.72rem",
1033
- letterSpacing: "0.18em",
1034
- textTransform: "uppercase",
1035
- color: "var(--md-primary)",
1036
- margin: 0
1037
- };
1038
- function OverviewSection({ config }) {
1039
- return /* @__PURE__ */ jsxs("section", { children: [
1040
- /* @__PURE__ */ jsx("p", { style: eyebrow5, children: "Brand kit" }),
1041
- /* @__PURE__ */ jsxs(
963
+ /* @__PURE__ */ jsx("p", { style: eyebrow4, children: "Brand kit" }),
964
+ lockup ? /* @__PURE__ */ jsx("div", { style: { margin: "1.25rem 0 0", maxWidth: "22rem" }, children: lockup }) : /* @__PURE__ */ jsxs(
1042
965
  "h1",
1043
966
  {
1044
967
  style: {
@@ -1067,7 +990,7 @@ function OverviewSection({ config }) {
1067
990
  }
1068
991
  ) : null,
1069
992
  config.voice && config.voice.length > 0 ? /* @__PURE__ */ jsxs("div", { style: { marginTop: "2.5rem" }, children: [
1070
- /* @__PURE__ */ jsx("p", { style: { ...eyebrow5, fontSize: "0.7rem", letterSpacing: "0.16em" }, children: "Voice" }),
993
+ /* @__PURE__ */ jsx("p", { style: { ...eyebrow4, fontSize: "0.7rem", letterSpacing: "0.16em" }, children: "Voice" }),
1071
994
  /* @__PURE__ */ jsx(
1072
995
  "ul",
1073
996
  {
@@ -1249,7 +1172,6 @@ function resolveSection(route) {
1249
1172
  case BrandStudioSection.Colors:
1250
1173
  case BrandStudioSection.Typography:
1251
1174
  case BrandStudioSection.Icons:
1252
- case BrandStudioSection.Motion:
1253
1175
  case BrandStudioSection.Download:
1254
1176
  return head;
1255
1177
  default:
@@ -1263,7 +1185,9 @@ function BrandStudio({
1263
1185
  staticLogo,
1264
1186
  staticLogoDark,
1265
1187
  animatedLogo,
1266
- icons
1188
+ icons,
1189
+ wordmark,
1190
+ wordmarkDark
1267
1191
  }) {
1268
1192
  const section = resolveSection(route);
1269
1193
  const scheme = config.scheme ?? generateScheme(config.color);
@@ -1280,11 +1204,13 @@ function BrandStudio({
1280
1204
  {
1281
1205
  href,
1282
1206
  style: {
1207
+ display: "inline-block",
1283
1208
  color: isActive ? "var(--md-primary)" : "var(--md-on-surface-variant)",
1284
1209
  textDecoration: "none",
1285
1210
  fontWeight: isActive ? 600 : 400,
1286
1211
  borderBottom: isActive ? "2px solid var(--md-primary)" : "2px solid transparent",
1287
- paddingBottom: "0.4rem"
1212
+ paddingBottom: "0.6rem",
1213
+ marginBottom: "-1px"
1288
1214
  },
1289
1215
  children
1290
1216
  }
@@ -1308,18 +1234,19 @@ function BrandStudio({
1308
1234
  /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: cssText } }),
1309
1235
  /* @__PURE__ */ jsx(Navigation, { current: section, basePath, renderLink: link }),
1310
1236
  /* @__PURE__ */ jsxs("div", { style: { marginTop: "3rem" }, children: [
1311
- section === BrandStudioSection.Overview && /* @__PURE__ */ jsx(OverviewSection, { config }),
1237
+ section === BrandStudioSection.Overview && /* @__PURE__ */ jsx(OverviewSection, { config, lockup: wordmark }),
1312
1238
  section === BrandStudioSection.Logo && /* @__PURE__ */ jsx(LogoSection, { config, staticLogo, animatedLogo }),
1313
1239
  section === BrandStudioSection.Colors && /* @__PURE__ */ jsx(ColorsSection, { scheme }),
1314
1240
  section === BrandStudioSection.Typography && /* @__PURE__ */ jsx(TypographySection, { config }),
1315
1241
  section === BrandStudioSection.Icons && /* @__PURE__ */ jsx(IconsSection, { staticLogo, icons }),
1316
- section === BrandStudioSection.Motion && /* @__PURE__ */ jsx(MotionPlaygroundSection, { animatedLogo, staticLogo }),
1317
1242
  section === BrandStudioSection.Download && /* @__PURE__ */ jsx(
1318
1243
  DownloadSection,
1319
1244
  {
1320
1245
  config,
1321
1246
  staticLogo,
1322
- staticLogoDark
1247
+ staticLogoDark,
1248
+ wordmark,
1249
+ wordmarkDark
1323
1250
  }
1324
1251
  )
1325
1252
  ] }),
@@ -1343,7 +1270,6 @@ function BrandStudio({
1343
1270
  "Powered by ",
1344
1271
  /* @__PURE__ */ jsx("span", { style: { color: "var(--md-primary)" }, children: "@021.is/brand-studio" })
1345
1272
  ] }),
1346
- config.operator ? /* @__PURE__ */ jsx("span", { children: config.operator }) : null,
1347
1273
  config.domain ? /* @__PURE__ */ jsx("span", { children: config.domain }) : null
1348
1274
  ]
1349
1275
  }
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { B as BrandConfig, a as BrandDownload, b as BrandDownloads, c as BrandSt
5
5
  export { B as BrandScheme, C as ColorScheme, a as ColorSpec, H as Hex, R as ROLE, b as ROLE_GROUPS, c as ROLE_ORDER, d as Role, S as SchemeVariant, g as generateScheme } from './generateScheme-BDDcIzA3.js';
6
6
  export { cssVarName, schemeToCssText, schemeVars } from './color/index.js';
7
7
  export { C as ContrastTier, c as contrastBadge, a as contrastRatio, r as relativeLuminance } from './contrast-TVW3pzdd.js';
8
- export { a as BrandStudio, b as BrandStudioProps } from './BrandStudio-D1QR4hIC.js';
8
+ export { a as BrandStudio, b as BrandStudioProps } from './BrandStudio-ksHBhiX3.js';
9
9
  export { STANDARD_ICON_SIZES, STANDARD_LOGO_SIZES, StandardDownloadsInput, buildStandardDownloads, defineBrand, slugify } from './define/index.js';
10
10
  import 'react/jsx-runtime';
11
11
  import 'react';
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  export { Disc, Ring, Squircle } from './chunk-LJ4HZCAP.js';
3
3
  export { AppearGrow, Drift, PulseLoop, RingDraw, Sequence, SequenceStep, Strikethrough, Sweep, Wobble } from './chunk-7325RQRP.js';
4
4
  export { LoadingMark, PulseLoadingMark, RingLoadingMark } from './chunk-F4CFQDS7.js';
5
- export { BrandStudio } from './chunk-L4FQAO3K.js';
5
+ export { BrandStudio } from './chunk-H3LCFXCK.js';
6
6
  export { SpinnerLoadingMark } from './chunk-YDZA26YU.js';
7
7
  export { STANDARD_ICON_SIZES, STANDARD_LOGO_SIZES, buildStandardDownloads, defineBrand, slugify } from './chunk-6J2NFZLN.js';
8
8
  export { BrandStudioSection, DownloadFormat, DownloadKind, DownloadMode, LoadingVariant, MotionState } from './chunk-MDTU2JR5.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@021.is/brand-studio",
3
- "version": "0.6.1",
4
- "description": "Generic MD3 brand-kit toolkit: seed full light + dark role set, composable SVG shapes + motion + loading marks, mountable <BrandStudio> /brand page, mandatory icon + logo download-URL contract. Ships no brand content every app supplies its own logo, icons, and seed colour.",
3
+ "version": "0.6.7",
4
+ "description": "Generic MD3 brand-kit toolkit: seed \u2192 full light + dark role set, composable SVG shapes + motion + loading marks, mountable <BrandStudio> /brand page, mandatory icon + logo download-URL contract. Ships no brand content \u2014 every app supplies its own logo, icons, and seed colour.",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "edvone",
@@ -30,7 +30,12 @@
30
30
  "private": false,
31
31
  "type": "module",
32
32
  "sideEffects": false,
33
- "files": ["dist", "LICENSE", "README.md", "AGENTS.md"],
33
+ "files": [
34
+ "dist",
35
+ "LICENSE",
36
+ "README.md",
37
+ "AGENTS.md"
38
+ ],
34
39
  "exports": {
35
40
  ".": {
36
41
  "types": "./dist/index.d.ts",