@syscore/ui-library 1.2.3 → 1.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.
@@ -84,6 +84,9 @@ export const conceptColors = {
84
84
  } as const;
85
85
 
86
86
 
87
+ const DEFAULT_FILL = "#EFF5FB";
88
+ const DEFAULT_STROKE = "#2E74AD";
89
+
87
90
  interface ConceptIconProps {
88
91
  className?: string;
89
92
  active?: boolean;
@@ -99,8 +102,8 @@ export const IconConceptMind: React.FC<ConceptIconProps> = ({
99
102
  outlined = false,
100
103
  }) => {
101
104
  const conceptColor = conceptColors.mind.solid;
102
- const bgFill = outlined ? "white" : active ? conceptColor : "#EFF5FB";
103
- const strokeColor = outlined ? conceptColor : active ? "white" : "#2E74AD";
105
+ const bgFill = outlined ? "white" : active ? conceptColor : DEFAULT_FILL;
106
+ const strokeColor = outlined ? conceptColor : active ? "white" : DEFAULT_STROKE;
104
107
 
105
108
  return (
106
109
  <svg
@@ -196,8 +199,8 @@ export const IconConceptCommunity: React.FC<ConceptIconProps> = ({
196
199
  outlined = false,
197
200
  }) => {
198
201
  const conceptColor = conceptColors.community.solid;
199
- const bgFill = outlined ? "white" : active ? conceptColor : "#EFF5FB";
200
- const strokeColor = outlined ? conceptColor : active ? "white" : "#2E74AD";
202
+ const bgFill = outlined ? "white" : active ? conceptColor : DEFAULT_FILL;
203
+ const strokeColor = outlined ? conceptColor : active ? "white" : DEFAULT_STROKE;
201
204
 
202
205
  return (
203
206
  <svg
@@ -253,8 +256,8 @@ export const IconConceptMovement: React.FC<ConceptIconProps> = ({
253
256
  outlined = false,
254
257
  }) => {
255
258
  const conceptColor = conceptColors.movement.solid;
256
- const bgFill = outlined ? "white" : active ? conceptColor : "#EFF5FB";
257
- const strokeColor = outlined ? conceptColor : active ? "white" : "#2E74AD";
259
+ const bgFill = outlined ? "white" : active ? conceptColor : DEFAULT_FILL;
260
+ const strokeColor = outlined ? conceptColor : active ? "white" : DEFAULT_STROKE;
258
261
 
259
262
  return (
260
263
  <svg
@@ -286,8 +289,8 @@ export const IconConceptWater: React.FC<ConceptIconProps> = ({
286
289
  outlined = false,
287
290
  }) => {
288
291
  const conceptColor = conceptColors.water.solid;
289
- const bgFill = outlined ? "white" : active ? conceptColor : "#EFF5FB";
290
- const strokeColor = outlined ? conceptColor : active ? "white" : "#2E74AD";
292
+ const bgFill = outlined ? "white" : active ? conceptColor : DEFAULT_FILL;
293
+ const strokeColor = outlined ? conceptColor : active ? "white" : DEFAULT_STROKE;
291
294
 
292
295
  return (
293
296
  <svg
@@ -327,8 +330,8 @@ export const IconConceptAir: React.FC<ConceptIconProps> = ({
327
330
  outlined = false,
328
331
  }) => {
329
332
  const conceptColor = conceptColors.air.contrast || conceptColors.air.solid;
330
- const bgFill = outlined ? "white" : active ? conceptColor : "#EFF5FB";
331
- const strokeColor = outlined ? conceptColor : active ? "white" : "#2E74AD";
333
+ const bgFill = outlined ? "white" : active ? conceptColor : DEFAULT_FILL;
334
+ const strokeColor = outlined ? conceptColor : active ? "white" : DEFAULT_STROKE;
332
335
 
333
336
  return (
334
337
  <svg
@@ -376,8 +379,8 @@ export const IconConceptLight: React.FC<ConceptIconProps> = ({
376
379
  outlined = false,
377
380
  }) => {
378
381
  const conceptColor = conceptColors.light.contrast || conceptColors.light.solid;
379
- const bgFill = outlined ? "white" : active ? conceptColor : "#EFF5FB";
380
- const strokeColor = outlined ? conceptColor : active ? "white" : "#2E74AD";
382
+ const bgFill = outlined ? "white" : active ? conceptColor : DEFAULT_FILL;
383
+ const strokeColor = outlined ? conceptColor : active ? "white" : DEFAULT_STROKE;
381
384
 
382
385
  return (
383
386
  <svg
@@ -472,9 +475,10 @@ export const IconConceptThermalComfort: React.FC<ConceptIconProps> = ({
472
475
  active = true,
473
476
  outlined = false,
474
477
  }) => {
475
- const conceptColor = conceptColors.thermalComfort.contrast || conceptColors.thermalComfort.solid;
476
- const bgFill = outlined ? "white" : active ? conceptColor : "#EFF5FB";
477
- const strokeColor = outlined ? conceptColor : active ? "white" : "#2E74AD";
478
+ const conceptColor =
479
+ conceptColors.thermalComfort?.contrast || conceptColors.thermalComfort.solid;
480
+ const bgFill = outlined ? "white" : active ? conceptColor : DEFAULT_FILL;
481
+ const strokeColor = outlined ? conceptColor : active ? "white" : DEFAULT_STROKE;
478
482
 
479
483
  return (
480
484
  <svg
@@ -586,8 +590,8 @@ export const IconConceptNourishment: React.FC<ConceptIconProps> = ({
586
590
  outlined = false,
587
591
  }) => {
588
592
  const conceptColor = conceptColors.nourishment.solid;
589
- const bgFill = outlined ? "white" : active ? conceptColor : "#EFF5FB";
590
- const strokeColor = outlined ? conceptColor : active ? "white" : "#2E74AD";
593
+ const bgFill = outlined ? "white" : active ? conceptColor : DEFAULT_FILL;
594
+ const strokeColor = outlined ? conceptColor : active ? "white" : DEFAULT_STROKE;
591
595
 
592
596
  return (
593
597
  <svg
@@ -643,8 +647,8 @@ export const IconConceptSound: React.FC<ConceptIconProps> = ({
643
647
  outlined = false,
644
648
  }) => {
645
649
  const conceptColor = conceptColors.sound.solid;
646
- const bgFill = outlined ? "white" : active ? conceptColor : "#EFF5FB";
647
- const strokeColor = outlined ? conceptColor : active ? "white" : "#2E74AD";
650
+ const bgFill = outlined ? "white" : active ? conceptColor : DEFAULT_FILL;
651
+ const strokeColor = outlined ? conceptColor : active ? "white" : DEFAULT_STROKE;
648
652
 
649
653
  return (
650
654
  <svg
@@ -716,8 +720,8 @@ export const IconConceptMaterials: React.FC<ConceptIconProps> = ({
716
720
  outlined = false,
717
721
  }) => {
718
722
  const conceptColor = conceptColors.materials.solid;
719
- const bgFill = outlined ? "white" : active ? conceptColor : "#EFF5FB";
720
- const strokeColor = outlined ? conceptColor : active ? "white" : "#2E74AD";
723
+ const bgFill = outlined ? "white" : active ? conceptColor : DEFAULT_FILL;
724
+ const strokeColor = outlined ? conceptColor : active ? "white" : DEFAULT_STROKE;
721
725
 
722
726
  return (
723
727
  <svg
@@ -773,8 +777,8 @@ export const IconConceptInnovation: React.FC<ConceptIconProps> = ({
773
777
  outlined = false,
774
778
  }) => {
775
779
  const conceptColor = conceptColors.innovation.solid;
776
- const bgFill = outlined ? "white" : active ? conceptColor : "#EFF5FB";
777
- const strokeColor = outlined ? conceptColor : active ? "white" : "#2E74AD";
780
+ const bgFill = outlined ? "white" : active ? conceptColor : DEFAULT_FILL;
781
+ const strokeColor = outlined ? conceptColor : active ? "white" : DEFAULT_STROKE;
778
782
 
779
783
  return (
780
784
  <svg
@@ -16,7 +16,7 @@ const getStatusClass = (
16
16
  status: TagStatus,
17
17
  variant: "light" | "dark" = "light"
18
18
  ) => {
19
- return `tag tag--${variant}-${status}`;
19
+ return `status-tag tag--${variant}-${status}`;
20
20
  };
21
21
 
22
22
  export const Tag = React.forwardRef<HTMLButtonElement, TagProps>(
@@ -0,0 +1,116 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+
4
+ import { cn } from "@/lib/utils";
5
+
6
+ const typographyVariants = cva("", {
7
+ variants: {
8
+ variant: {
9
+ "heading-large": "heading-large",
10
+ "heading-medium": "heading-medium",
11
+ "heading-small": "heading-small",
12
+ "heading-xsmall": "heading-xsmall",
13
+ "heading-xxsmall": "heading-xxsmall",
14
+ "body-large": "body-large",
15
+ "body-base": "body-base",
16
+ "body-small": "body-small",
17
+ "overline-large": "overline-large",
18
+ "overline-medium": "overline-medium",
19
+ "overline-small": "overline-small",
20
+ "number-large": "number-large",
21
+ "number-base": "number-base",
22
+ },
23
+ },
24
+ defaultVariants: {
25
+ variant: "body-base",
26
+ },
27
+ });
28
+
29
+ type Variant = NonNullable<VariantProps<typeof typographyVariants>["variant"]>;
30
+
31
+ const defaultElementMap: Record<Variant, React.ElementType> = {
32
+ "heading-large": "h1",
33
+ "heading-medium": "h2",
34
+ "heading-small": "h3",
35
+ "heading-xsmall": "h4",
36
+ "heading-xxsmall": "h5",
37
+ "body-large": "p",
38
+ "body-base": "p",
39
+ "body-small": "p",
40
+ "overline-large": "span",
41
+ "overline-medium": "span",
42
+ "overline-small": "span",
43
+ "number-large": "span",
44
+ "number-base": "span",
45
+ };
46
+
47
+ interface TextProps extends React.HTMLAttributes<HTMLElement> {
48
+ variant?: Variant;
49
+ as?: React.ElementType;
50
+ }
51
+
52
+ const Text = React.forwardRef<HTMLElement, TextProps>(
53
+ ({ className, variant = "body-base", as, ...props }, ref) => {
54
+ const Component = as || defaultElementMap[variant];
55
+
56
+ return (
57
+ <Component
58
+ ref={ref}
59
+ className={cn(typographyVariants({ variant }), className)}
60
+ {...props}
61
+ />
62
+ );
63
+ }
64
+ );
65
+ Text.displayName = "Text";
66
+
67
+ interface ListProps extends React.HTMLAttributes<HTMLUListElement> {}
68
+
69
+ const List = React.forwardRef<HTMLUListElement, ListProps>(
70
+ ({ className, ...props }, ref) => {
71
+ return (
72
+ <ul
73
+ ref={ref}
74
+ className={cn("typography-list", className)}
75
+ {...props}
76
+ />
77
+ );
78
+ }
79
+ );
80
+ List.displayName = "List";
81
+
82
+ interface ListItemProps extends React.HTMLAttributes<HTMLLIElement> {
83
+ variant?: Variant;
84
+ }
85
+
86
+ const ListItem = React.forwardRef<HTMLLIElement, ListItemProps>(
87
+ ({ className, variant = "body-base", children, ...props }, ref) => {
88
+ return (
89
+ <li
90
+ ref={ref}
91
+ className={cn("typography-list-item", className)}
92
+ {...props}
93
+ >
94
+ <svg
95
+ xmlns="http://www.w3.org/2000/svg"
96
+ width="4"
97
+ height="4"
98
+ viewBox="0 0 4 4"
99
+ fill="none"
100
+ className="typography-list-bullet"
101
+ aria-hidden="true"
102
+ >
103
+ <circle cx="2" cy="2" r="2" fill="#71747D" />
104
+ </svg>
105
+ <span className={cn(typographyVariants({ variant }))}>
106
+ {children}
107
+ </span>
108
+ </li>
109
+ );
110
+ }
111
+ );
112
+ ListItem.displayName = "ListItem";
113
+
114
+ export { Text, List, ListItem, typographyVariants, defaultElementMap };
115
+ export type { TextProps, ListProps, ListItemProps, Variant as TypographyVariant };
116
+
package/client/global.css CHANGED
@@ -699,7 +699,7 @@
699
699
  }
700
700
 
701
701
  /* Tag Styles */
702
- .tag {
702
+ .status-tag {
703
703
  display: inline-flex;
704
704
  align-items: center;
705
705
  padding: 8px;
@@ -709,14 +709,14 @@
709
709
  transition: all 0.2s ease-in-out;
710
710
  }
711
711
 
712
- .tag:focus-visible {
712
+ .status-tag:focus-visible {
713
713
  outline: none;
714
714
  box-shadow:
715
715
  0 0 0 2px hsl(var(--ring)),
716
716
  0 0 0 4px var(--color-white, #fff);
717
717
  }
718
718
 
719
- .tag:disabled {
719
+ .status-tag:disabled {
720
720
  opacity: 0.5;
721
721
  cursor: not-allowed;
722
722
  }
@@ -5528,3 +5528,22 @@
5528
5528
  width: 1rem;
5529
5529
  height: 1rem;
5530
5530
  }
5531
+
5532
+ /* Typography List Styles */
5533
+ .typography-list {
5534
+ display: flex;
5535
+ flex-direction: column;
5536
+ gap: 1rem;
5537
+ padding-left: 0.5rem;
5538
+ margin-top: 1rem;
5539
+ }
5540
+
5541
+ .typography-list-item {
5542
+ display: flex;
5543
+ align-items: center;
5544
+ }
5545
+
5546
+ .typography-list-bullet {
5547
+ flex-shrink: 0;
5548
+ margin-right: 1rem;
5549
+ }
@@ -19,7 +19,20 @@ import {
19
19
  } from "../components/ui/select";
20
20
  import { Label } from "../components/ui/label";
21
21
  import { Toggle } from "../components/ui/toggle";
22
-
22
+ import {
23
+ IconConceptMind,
24
+ IconConceptCommunity,
25
+ IconConceptMovement,
26
+ IconConceptWater,
27
+ IconConceptAir,
28
+ IconConceptLight,
29
+ IconConceptThermalComfort,
30
+ IconConceptNourishment,
31
+ IconConceptSound,
32
+ IconConceptMaterials,
33
+ IconConceptInnovation,
34
+ } from "../components/icons/ConceptIcons";
35
+ import { Text } from "../components/ui/typography";
23
36
  import { UtilityReset } from "../components/icons/UtilityReset";
24
37
 
25
38
  const meta = {
@@ -192,20 +205,6 @@ export const FiltersPanel: Story = {
192
205
  // },
193
206
  // };
194
207
 
195
- import {
196
- IconConceptAir,
197
- IconConceptCommunity,
198
- IconConceptLight,
199
- IconConceptMaterials,
200
- IconConceptMind,
201
- IconConceptMovement,
202
- IconConceptNourishment,
203
- IconConceptSound,
204
- IconConceptThermalComfort,
205
- IconConceptWater,
206
- } from "../components/icons/ConceptIcons";
207
-
208
- // ... existing imports
209
208
 
210
209
  export const NavigatorPanel: Story = {
211
210
  render: () => {
@@ -255,11 +254,10 @@ export const NavigatorPanel: Story = {
255
254
  <button
256
255
  key={theme}
257
256
  onClick={() => setActiveTheme(theme)}
258
- className={`w-12 h-8 rounded-[6px] flex items-center justify-center transition-colors ${
259
- activeTheme === theme
260
- ? "bg-cyan-800 text-white"
261
- : "bg-blue-100 text-blue-600 hover:bg-blue-200"
262
- }`}
257
+ className={`w-12 h-8 rounded-[6px] flex items-center justify-center transition-colors ${activeTheme === theme
258
+ ? "bg-cyan-800 text-white"
259
+ : "bg-blue-100 text-blue-600 hover:bg-blue-200"
260
+ }`}
263
261
  >
264
262
  <span className="body-small font-semibold">{theme}</span>
265
263
  </button>
@@ -275,11 +273,10 @@ export const NavigatorPanel: Story = {
275
273
  <button
276
274
  key={strategy}
277
275
  onClick={() => setActiveStrategy(strategy)}
278
- className={`w-12 h-8 rounded-[6px] flex items-center justify-center transition-colors ${
279
- activeStrategy === strategy
280
- ? "bg-[#0F748A]/10 border border-[#0F748A]/20 text-cyan-900"
281
- : "bg-blue-100 text-blue-600 hover:bg-blue-200"
282
- }`}
276
+ className={`w-12 h-8 rounded-[6px] flex items-center justify-center transition-colors ${activeStrategy === strategy
277
+ ? "bg-[#0F748A]/10 border border-[#0F748A]/20 text-cyan-900"
278
+ : "bg-blue-100 text-blue-600 hover:bg-blue-200"
279
+ }`}
283
280
  >
284
281
  <span className="body-small font-semibold">{strategy}</span>
285
282
  </button>
@@ -303,3 +300,124 @@ export const NavigatorPanel: Story = {
303
300
  );
304
301
  },
305
302
  };
303
+
304
+
305
+ export const ConceptCard: Story = {
306
+ render: () => {
307
+ const CONCEPTS_MOCK = [
308
+ {
309
+ id: "air",
310
+ slug: "air",
311
+ name: "Air",
312
+ title: "AIR",
313
+ description: "Reduce or minimize sources of indoor air pollution",
314
+ },
315
+ {
316
+ id: "water",
317
+ slug: "water",
318
+ name: "Water",
319
+ title: "WATER",
320
+ description: "Increase hydration and access to high quality water",
321
+ },
322
+ {
323
+ id: "nourishment",
324
+ slug: "nourishment",
325
+ name: "Nourishment",
326
+ title: "NOURISHMENT",
327
+ description:
328
+ "Make healthy foods the easy decision and encourage better food choices",
329
+ },
330
+ {
331
+ id: "light",
332
+ slug: "light",
333
+ name: "Light",
334
+ title: "LIGHT",
335
+ description:
336
+ "Design lighting to increase alertness, enhance occupant experience, and promote optimal sleep patterns",
337
+ },
338
+ {
339
+ id: "movement",
340
+ slug: "movement",
341
+ name: "Movement",
342
+ title: "MOVEMENT",
343
+ description: "Integrate physical activity and fitness into everyday life",
344
+ },
345
+ {
346
+ id: "thermal-comfort",
347
+ slug: "thermal-comfort",
348
+ name: "Thermal Comfort",
349
+ title: "THERMAL COMFORT",
350
+ description: "Provide productive and comfortable indoor environments",
351
+ },
352
+ {
353
+ id: "sound",
354
+ slug: "sound",
355
+ name: "Sound",
356
+ title: "SOUND",
357
+ description:
358
+ "Support optimal acoustical comfort to reduce distractions and promote focus",
359
+ },
360
+ {
361
+ id: "materials",
362
+ slug: "materials",
363
+ name: "Materials",
364
+ title: "MATERIALS",
365
+ description:
366
+ "Improve respiratory health through use of safe materials and finishes",
367
+ },
368
+ {
369
+ id: "community",
370
+ slug: "community",
371
+ name: "Community",
372
+ title: "COMMUNITY",
373
+ description: "Foster community engagement and social support",
374
+ },
375
+ {
376
+ id: "mind",
377
+ slug: "mind",
378
+ name: "Mind",
379
+ title: "MIND",
380
+ description:
381
+ "Support cognitive and emotional health through design, technology, and treatment strategies",
382
+ },
383
+ {
384
+ id: "innovation",
385
+ slug: "innovation",
386
+ name: "Innovation",
387
+ title: "INNOVATION",
388
+ description: "Develop unique strategies for creating healthy environments",
389
+ },
390
+ ];
391
+
392
+ const CONCEPT_ICONS = {
393
+ air: IconConceptAir,
394
+ water: IconConceptWater,
395
+ nourishment: IconConceptNourishment,
396
+ light: IconConceptLight,
397
+ movement: IconConceptMovement,
398
+ "thermal-comfort": IconConceptThermalComfort,
399
+ sound: IconConceptSound,
400
+ materials: IconConceptMaterials,
401
+ community: IconConceptCommunity,
402
+ mind: IconConceptMind,
403
+ innovation: IconConceptInnovation,
404
+ };
405
+
406
+ return (
407
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
408
+ {CONCEPTS_MOCK.map((concept) => {
409
+ const Icon = CONCEPT_ICONS[concept.slug as keyof typeof CONCEPT_ICONS];
410
+ return (
411
+ <Card key={concept.id} className=" border-gray-100 bg-white p-6 gap-6 hover:scale-102 hover:border-gray-200 ease-in-out duration-200 will-change-transform">
412
+ <div className="flex items-center gap-4">
413
+ {Icon && <Icon className="size-12" />}
414
+ <Text as="h3" variant="overline-large">{concept.title}</Text>
415
+ </div>
416
+ <Text as="p" variant="body-base">{concept.description}</Text>
417
+ </Card>
418
+ );
419
+ })}
420
+ </div>
421
+ );
422
+ },
423
+ };