@syscore/ui-library 1.8.0 → 1.9.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.
@@ -1,53 +1,509 @@
1
- import * as React from "react";
2
- import * as AccordionPrimitive from "@radix-ui/react-accordion";
3
- import { ChevronDown } from "lucide-react";
1
+ "use client";
4
2
 
3
+ import * as React from "react";
4
+ import { motion, AnimatePresence } from "motion/react";
5
5
  import { cn } from "@/lib/utils";
6
+ import { Button } from "./button";
7
+ import { UtilityChevronDown } from "../icons/UtilityChevronDown";
8
+
9
+ // Context Definitions
10
+ interface AccordionContextValue {
11
+ /** Set of currently expanded item values */
12
+ expandedValues: Set<string>;
13
+ /** Check if an item is expanded */
14
+ isExpanded: (value: string) => boolean;
15
+ /** Toggle a specific item's expansion */
16
+ toggle: (value: string) => void;
17
+ /** Expand all items */
18
+ expandAll: () => void;
19
+ /** Collapse all items */
20
+ collapseAll: () => void;
21
+ /** Check if any item is expanded */
22
+ hasAnyExpanded: boolean;
23
+ /** Allow multiple items to be expanded at once */
24
+ allowMultiple: boolean;
25
+ }
26
+
27
+ const AccordionContext = React.createContext<AccordionContextValue | null>(
28
+ null,
29
+ );
30
+
31
+ interface AccordionItemContextValue {
32
+ /** Current item's value */
33
+ value: string;
34
+ /** Whether this item is expanded */
35
+ isExpanded: boolean;
36
+ }
37
+
38
+ const AccordionItemContext =
39
+ React.createContext<AccordionItemContextValue | null>(null);
40
+
41
+ // Hooks
42
+ function useAccordion() {
43
+ const context = React.useContext(AccordionContext);
44
+ if (!context) {
45
+ throw new Error("Accordion components must be used within <Accordion>");
46
+ }
47
+ return context;
48
+ }
49
+
50
+ function useAccordionItem() {
51
+ const context = React.useContext(AccordionItemContext);
52
+ if (!context) {
53
+ throw new Error(
54
+ "AccordionTrigger and AccordionContent must be used within <AccordionItem>",
55
+ );
56
+ }
57
+ return context;
58
+ }
59
+
60
+ // useAccordionState Hook (for external state management)
61
+ interface UseAccordionStateOptions {
62
+ /** Allow multiple items to be expanded at once */
63
+ allowMultiple?: boolean;
64
+ /** Initially expanded item value(s) */
65
+ defaultExpanded?: string | string[];
66
+ }
67
+
68
+ interface UseAccordionStateReturn {
69
+ /** Current expanded values */
70
+ expandedValues: Set<string>;
71
+ /** Set expanded values */
72
+ setExpandedValues: (values: Set<string>) => void;
73
+ /** Check if any item is expanded */
74
+ hasAnyExpanded: boolean;
75
+ /** Toggle all items */
76
+ toggleAll: () => void;
77
+ }
78
+
79
+ function useAccordionState(
80
+ options: UseAccordionStateOptions = {},
81
+ ): UseAccordionStateReturn {
82
+ const { allowMultiple = false, defaultExpanded } = options;
83
+
84
+ // Initialize expanded values from default
85
+ const getInitialExpanded = (): Set<string> => {
86
+ if (!defaultExpanded) return new Set();
87
+ if (Array.isArray(defaultExpanded)) return new Set(defaultExpanded);
88
+ return new Set([defaultExpanded]);
89
+ };
90
+
91
+ const [expandedValues, setExpandedValues] =
92
+ React.useState<Set<string>>(getInitialExpanded);
93
+ const [isAllExpanded, setIsAllExpanded] = React.useState(false);
94
+
95
+ const hasAnyExpanded = React.useMemo(
96
+ () => isAllExpanded || expandedValues.size > 0,
97
+ [isAllExpanded, expandedValues],
98
+ );
99
+
100
+ const toggleAll = React.useCallback(() => {
101
+ if (hasAnyExpanded) {
102
+ setIsAllExpanded(false);
103
+ setExpandedValues(new Set());
104
+ } else {
105
+ setIsAllExpanded(true);
106
+ setExpandedValues(new Set());
107
+ }
108
+ }, [hasAnyExpanded]);
109
+
110
+ return {
111
+ expandedValues,
112
+ setExpandedValues,
113
+ hasAnyExpanded,
114
+ toggleAll,
115
+ };
116
+ }
117
+
118
+ // Accordion (Root Component)
119
+ interface AccordionProps extends React.ComponentPropsWithoutRef<"div"> {
120
+ /** Allow multiple items to be expanded at once (default: false for accordion behavior) */
121
+ allowMultiple?: boolean;
122
+ /** Initially expanded item value(s) (uncontrolled) */
123
+ defaultExpanded?: string | string[];
124
+ /** Controlled expanded values */
125
+ expandedValues?: Set<string>;
126
+ /** Controlled callback when expanded values change */
127
+ onExpandedChange?: (values: Set<string>) => void;
128
+ }
129
+
130
+ const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
131
+ (
132
+ {
133
+ allowMultiple = false,
134
+ defaultExpanded,
135
+ expandedValues: controlledExpandedValues,
136
+ onExpandedChange,
137
+ children,
138
+ className,
139
+ ...props
140
+ },
141
+ ref,
142
+ ) => {
143
+ // Initialize expanded values from default
144
+ const getInitialExpanded = (): Set<string> => {
145
+ if (!defaultExpanded) return new Set();
146
+ if (Array.isArray(defaultExpanded)) return new Set(defaultExpanded);
147
+ return new Set([defaultExpanded]);
148
+ };
149
+
150
+ const [uncontrolledExpandedValues, setUncontrolledExpandedValues] =
151
+ React.useState<Set<string>>(getInitialExpanded);
152
+ const [isAllExpanded, setIsAllExpanded] = React.useState(false);
153
+
154
+ // Determine if controlled
155
+ const isControlled = controlledExpandedValues !== undefined;
156
+ const expandedValues = isControlled
157
+ ? controlledExpandedValues
158
+ : uncontrolledExpandedValues;
159
+
160
+ // Check if any item is expanded
161
+ const hasAnyExpanded = React.useMemo(
162
+ () => isAllExpanded || expandedValues.size > 0,
163
+ [isAllExpanded, expandedValues],
164
+ );
165
+
166
+ // Check if a specific item is expanded
167
+ const isExpanded = React.useCallback(
168
+ (value: string): boolean => isAllExpanded || expandedValues.has(value),
169
+ [isAllExpanded, expandedValues],
170
+ );
171
+
172
+ // Toggle a specific item
173
+ const toggle = React.useCallback(
174
+ (value: string) => {
175
+ const newValues = new Set(expandedValues);
176
+ if (newValues.has(value)) {
177
+ newValues.delete(value);
178
+ } else {
179
+ if (!allowMultiple) {
180
+ newValues.clear(); // Accordion mode: close all others
181
+ }
182
+ newValues.add(value);
183
+ }
184
+
185
+ if (!isControlled) {
186
+ setUncontrolledExpandedValues(newValues);
187
+ }
188
+ onExpandedChange?.(newValues);
189
+ setIsAllExpanded(false);
190
+ },
191
+ [allowMultiple, expandedValues, isControlled, onExpandedChange],
192
+ );
193
+
194
+ // Expand all items
195
+ const expandAll = React.useCallback(() => {
196
+ setIsAllExpanded(true);
197
+ const emptySet = new Set<string>();
198
+ if (!isControlled) {
199
+ setUncontrolledExpandedValues(emptySet);
200
+ }
201
+ onExpandedChange?.(emptySet);
202
+ }, [isControlled, onExpandedChange]);
203
+
204
+ // Collapse all items
205
+ const collapseAll = React.useCallback(() => {
206
+ setIsAllExpanded(false);
207
+ const emptySet = new Set<string>();
208
+ if (!isControlled) {
209
+ setUncontrolledExpandedValues(emptySet);
210
+ }
211
+ onExpandedChange?.(emptySet);
212
+ }, [isControlled, onExpandedChange]);
213
+
214
+ const contextValue = React.useMemo(
215
+ () => ({
216
+ expandedValues,
217
+ isExpanded,
218
+ toggle,
219
+ expandAll,
220
+ collapseAll,
221
+ hasAnyExpanded,
222
+ allowMultiple,
223
+ }),
224
+ [
225
+ expandedValues,
226
+ isExpanded,
227
+ toggle,
228
+ expandAll,
229
+ collapseAll,
230
+ hasAnyExpanded,
231
+ allowMultiple,
232
+ ],
233
+ );
234
+
235
+ return (
236
+ <AccordionContext.Provider value={contextValue}>
237
+ <div ref={ref} className={cn(className)} {...props}>
238
+ {children}
239
+ </div>
240
+ </AccordionContext.Provider>
241
+ );
242
+ },
243
+ );
244
+
245
+ Accordion.displayName = "Accordion";
246
+
247
+ // AccordionSectionHeader
248
+ interface AccordionSectionHeaderProps {
249
+ /** Header title text */
250
+ title: string;
251
+ /** Whether any items are expanded */
252
+ hasExpanded?: boolean;
253
+ /** Toggle all callback */
254
+ onToggleAll?: () => void;
255
+ /** Optional className for the container */
256
+ className?: string;
257
+ }
258
+
259
+ const AccordionSectionHeader = React.forwardRef<
260
+ HTMLDivElement,
261
+ AccordionSectionHeaderProps
262
+ >(({ title, hasExpanded, onToggleAll, className }, ref) => {
263
+ return (
264
+ <div ref={ref} className={cn(className)}>
265
+ <h2>{title}</h2>
266
+
267
+ {onToggleAll && (
268
+ <motion.div
269
+ animate={{ rotate: hasExpanded ? 180 : 0 }}
270
+ transition={{ duration: 0.2 }}
271
+ >
272
+ <Button size="icon" onClick={onToggleAll}>
273
+ <UtilityChevronDown />
274
+ </Button>
275
+ </motion.div>
276
+ )}
277
+ </div>
278
+ );
279
+ });
280
+
281
+ AccordionSectionHeader.displayName = "AccordionSectionHeader";
282
+
283
+ // AccordionHeaderRow (inside Accordion with context access)
284
+ interface AccordionHeaderRowProps {
285
+ /** Header title text */
286
+ title: string;
287
+ /** Optional className for the container */
288
+ className?: string;
289
+ }
290
+
291
+ const AccordionHeaderRow = React.forwardRef<
292
+ HTMLDivElement,
293
+ AccordionHeaderRowProps
294
+ >(({ title, className }, ref) => {
295
+ const { hasAnyExpanded, expandAll, collapseAll } = useAccordion();
296
+
297
+ const handleToggleAll = React.useCallback(() => {
298
+ if (hasAnyExpanded) {
299
+ collapseAll();
300
+ } else {
301
+ expandAll();
302
+ }
303
+ }, [hasAnyExpanded, collapseAll, expandAll]);
304
+
305
+ return (
306
+ <div ref={ref} className={cn(className)}>
307
+ <h2>{title}</h2>
308
+ <motion.div
309
+ animate={{ rotate: hasAnyExpanded ? 180 : 0 }}
310
+ transition={{ duration: 0.2 }}
311
+ style={{ willChange: "transform" }}
312
+ >
313
+ <Button size="icon" onClick={handleToggleAll}>
314
+ <UtilityChevronDown />
315
+ </Button>
316
+ </motion.div>
317
+ </div>
318
+ );
319
+ });
320
+
321
+ AccordionHeaderRow.displayName = "AccordionHeaderRow";
322
+
323
+ // AccordionItem
324
+ interface AccordionItemProps extends React.ComponentPropsWithoutRef<"div"> {
325
+ /** Unique value for this item */
326
+ value: string;
327
+ }
328
+
329
+ const AccordionItem = React.forwardRef<HTMLDivElement, AccordionItemProps>(
330
+ ({ value, children, className, ...props }, ref) => {
331
+ const { isExpanded: isExpandedFn } = useAccordion();
332
+ const isExpanded = isExpandedFn(value);
333
+
334
+ const itemContextValue = React.useMemo(
335
+ () => ({ value, isExpanded }),
336
+ [value, isExpanded],
337
+ );
338
+
339
+ return (
340
+ <AccordionItemContext.Provider value={itemContextValue}>
341
+ <div ref={ref} className={cn(className)} {...props}>
342
+ {children}
343
+ </div>
344
+ </AccordionItemContext.Provider>
345
+ );
346
+ },
347
+ );
6
348
 
7
- const Accordion = AccordionPrimitive.Root;
8
-
9
- const AccordionItem = React.forwardRef<
10
- React.ElementRef<typeof AccordionPrimitive.Item>,
11
- React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
12
- >(({ className, ...props }, ref) => (
13
- <AccordionPrimitive.Item
14
- ref={ref}
15
- className={cn("accordion-item", className)}
16
- {...props}
17
- />
18
- ));
19
349
  AccordionItem.displayName = "AccordionItem";
20
350
 
351
+ // AccordionHeader
352
+ type AccordionHeaderProps = React.ComponentPropsWithoutRef<"div">;
353
+
354
+ const AccordionHeader = React.forwardRef<HTMLDivElement, AccordionHeaderProps>(
355
+ ({ children, className, onClick, ...props }, ref) => {
356
+ return (
357
+ <div ref={ref} onClick={onClick} className={cn(className)} {...props}>
358
+ {children}
359
+ </div>
360
+ );
361
+ },
362
+ );
363
+
364
+ AccordionHeader.displayName = "AccordionHeader";
365
+
366
+ // AccordionTrigger (Chevron Button)
367
+ type AccordionTriggerProps = React.ComponentPropsWithoutRef<"button">;
368
+
21
369
  const AccordionTrigger = React.forwardRef<
22
- React.ElementRef<typeof AccordionPrimitive.Trigger>,
23
- React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
24
- >(({ className, children, ...props }, ref) => (
25
- <AccordionPrimitive.Header className="accordion-header">
26
- <AccordionPrimitive.Trigger
27
- ref={ref}
28
- className={cn("accordion-trigger", className)}
29
- {...props}
370
+ HTMLButtonElement,
371
+ AccordionTriggerProps
372
+ >(({ className, onClick, ...props }, ref) => {
373
+ const { toggle } = useAccordion();
374
+ const { value, isExpanded } = useAccordionItem();
375
+
376
+ const handleClick = React.useCallback(
377
+ (e: React.MouseEvent<HTMLButtonElement>) => {
378
+ e.stopPropagation(); // Prevent header click
379
+ toggle(value);
380
+ onClick?.(e);
381
+ },
382
+ [toggle, value, onClick],
383
+ );
384
+
385
+ return (
386
+ <motion.div
387
+ animate={{ rotate: isExpanded ? 180 : 0 }}
388
+ transition={{ duration: 0.2 }}
389
+ style={{ willChange: "transform" }}
30
390
  >
391
+ <Button
392
+ ref={ref}
393
+ size="icon"
394
+ variant="clear"
395
+ onClick={handleClick}
396
+ className={cn(className)}
397
+ {...props}
398
+ >
399
+ <UtilityChevronDown />
400
+ </Button>
401
+ </motion.div>
402
+ );
403
+ });
404
+
405
+ AccordionTrigger.displayName = "AccordionTrigger";
406
+
407
+ // AccordionContent (Expandable Content Area)
408
+ type AccordionContentProps = React.ComponentPropsWithoutRef<"div">;
409
+
410
+ const AccordionContent = React.forwardRef<HTMLDivElement, AccordionContentProps>(
411
+ ({ children, className, ...props }, ref) => {
412
+ const { isExpanded } = useAccordionItem();
413
+
414
+ return (
415
+ <AnimatePresence initial={false}>
416
+ {isExpanded && (
417
+ <motion.div
418
+ initial={{ height: 0, opacity: 0 }}
419
+ animate={{ height: "auto", opacity: 1 }}
420
+ exit={{ height: 0, opacity: 0 }}
421
+ transition={{ duration: 0.3, ease: [0.25, 0.46, 0.45, 0.94] }}
422
+ className={cn(className)}
423
+ style={{ willChange: "opacity" }}
424
+ >
425
+ <div ref={ref} {...props}>
426
+ {children}
427
+ </div>
428
+ </motion.div>
429
+ )}
430
+ </AnimatePresence>
431
+ );
432
+ },
433
+ );
434
+
435
+ AccordionContent.displayName = "AccordionContent";
436
+
437
+ // AccordionListRow (for nested items)
438
+ interface AccordionListRowProps extends React.ComponentPropsWithoutRef<"div"> {
439
+ /** Optional icon element */
440
+ icon?: React.ReactNode;
441
+ /** Optional badge element */
442
+ badge?: React.ReactNode;
443
+ /** Row title */
444
+ title: string;
445
+ /** Optional title className */
446
+ titleClassName?: string;
447
+ /** Optional right-side content */
448
+ rightContent?: React.ReactNode;
449
+ /** Visual variant */
450
+ variant?: "default" | "nested";
451
+ }
452
+
453
+ const AccordionListRow = React.forwardRef<HTMLDivElement, AccordionListRowProps>(
454
+ (
455
+ {
456
+ icon,
457
+ badge,
458
+ title,
459
+ titleClassName,
460
+ rightContent,
461
+ onClick,
462
+ className,
463
+ variant = "default",
464
+ ...props
465
+ },
466
+ ref,
467
+ ) => {
468
+ return (
469
+ <div ref={ref} onClick={onClick} className={cn(className)} {...props}>
470
+ {icon}
471
+ {badge}
472
+ <span className={cn(titleClassName)}>{title}</span>
473
+ {rightContent}
474
+ </div>
475
+ );
476
+ },
477
+ );
478
+
479
+ AccordionListRow.displayName = "AccordionListRow";
480
+
481
+ type AccordionContainerProps = React.ComponentPropsWithoutRef<"section">;
482
+
483
+ const AccordionContainer = React.forwardRef<
484
+ HTMLElement,
485
+ AccordionContainerProps
486
+ >(({ children, className, ...props }, ref) => {
487
+ return (
488
+ <section ref={ref} className={cn(className)} {...props}>
31
489
  {children}
32
- <ChevronDown className="accordion-chevron" />
33
- </AccordionPrimitive.Trigger>
34
- </AccordionPrimitive.Header>
35
- ));
36
- AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
37
-
38
- const AccordionContent = React.forwardRef<
39
- React.ElementRef<typeof AccordionPrimitive.Content>,
40
- React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
41
- >(({ className, children, ...props }, ref) => (
42
- <AccordionPrimitive.Content
43
- ref={ref}
44
- className="accordion-content"
45
- {...props}
46
- >
47
- <div className={cn("accordion-content-inner", className)}>{children}</div>
48
- </AccordionPrimitive.Content>
49
- ));
50
-
51
- AccordionContent.displayName = AccordionPrimitive.Content.displayName;
52
-
53
- export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
490
+ </section>
491
+ );
492
+ });
493
+
494
+ AccordionContainer.displayName = "AccordionContainer";
495
+
496
+ export {
497
+ useAccordionState,
498
+ useAccordion,
499
+ useAccordionItem,
500
+ Accordion,
501
+ AccordionSectionHeader,
502
+ AccordionHeaderRow,
503
+ AccordionItem,
504
+ AccordionHeader,
505
+ AccordionTrigger,
506
+ AccordionContent,
507
+ AccordionListRow,
508
+ AccordionContainer,
509
+ };
@@ -13,10 +13,10 @@ const Label = React.forwardRef<
13
13
  >(({ className, children, ...props }, ref) => (
14
14
  <LabelPrimitive.Root
15
15
  ref={ref}
16
- className={cn(labelVariants(), "overline-medium", className)}
16
+ className={cn(labelVariants(), className)}
17
17
  {...props}
18
18
  >
19
- {children}
19
+ <div className="overline-medium"> {children}</div>
20
20
  </LabelPrimitive.Root>
21
21
  ));
22
22
  Label.displayName = LabelPrimitive.Root.displayName;
@@ -2,44 +2,53 @@ import * as React from "react";
2
2
  import { cn } from "@/lib/utils";
3
3
 
4
4
  export type TagStatus = "todo" | "low" | "medium" | "high" | "done";
5
+ export type TagVariant = "text" | "code";
5
6
 
6
7
  export interface TagProps
7
8
  extends React.ButtonHTMLAttributes<HTMLButtonElement> {
8
9
  children: React.ReactNode;
10
+ /** Visual variant - "text" for normal tags, "code" for code badges */
11
+ variant?: TagVariant;
12
+ /** Active state for toggle behavior in forms/filters */
9
13
  active?: boolean;
14
+ /** Status for status-specific styling (overrides variant) */
10
15
  status?: TagStatus;
11
- variant?: "light" | "dark";
16
+ /** Color scheme for status tags */
17
+ colorScheme?: "light" | "dark";
12
18
  onClick?: () => void;
13
19
  }
14
20
 
15
21
  const getStatusClass = (
16
22
  status: TagStatus,
17
- variant: "light" | "dark" = "light",
23
+ colorScheme: "light" | "dark" = "light",
18
24
  ) => {
19
- return `status-tag tag--${variant}-${status}`;
25
+ return `status-tag tag--${colorScheme}-${status}`;
20
26
  };
21
27
 
22
28
  export const Tag = React.forwardRef<HTMLButtonElement, TagProps>(
23
29
  (
24
30
  {
25
31
  children,
32
+ variant = "text",
26
33
  active = false,
27
34
  status,
28
- variant = "light",
35
+ colorScheme = "light",
29
36
  className,
37
+ style,
30
38
  onClick,
31
39
  ...props
32
40
  },
33
41
  ref,
34
42
  ) => {
35
- // Status tag styling
43
+ // Status tag styling (highest priority)
36
44
  if (status) {
37
- const statusClass = getStatusClass(status, variant);
45
+ const statusClass = getStatusClass(status, colorScheme);
38
46
  return (
39
47
  <button
40
48
  ref={ref}
41
49
  onClick={onClick}
42
50
  className={cn("overline-medium", statusClass, className)}
51
+ style={style}
43
52
  {...props}
44
53
  >
45
54
  {children}
@@ -47,7 +56,24 @@ export const Tag = React.forwardRef<HTMLButtonElement, TagProps>(
47
56
  );
48
57
  }
49
58
 
50
- // Dual-state general purpose tag styling
59
+ // Code variant - for code badges like "C1", "A3"
60
+ if (variant === "code") {
61
+ return (
62
+ <button
63
+ ref={ref}
64
+ onClick={onClick}
65
+ className={cn("tag-code", className)}
66
+ style={style}
67
+ {...props}
68
+ >
69
+ <span className="number-small font-semibold" style={{ color: "inherit" }}>
70
+ {children}
71
+ </span>
72
+ </button>
73
+ );
74
+ }
75
+
76
+ // Text variant (default) - general purpose tag for forms/filters
51
77
  return (
52
78
  <button
53
79
  ref={ref}
@@ -57,6 +83,7 @@ export const Tag = React.forwardRef<HTMLButtonElement, TagProps>(
57
83
  active ? "tag-general--active" : "tag-general--inactive",
58
84
  className,
59
85
  )}
86
+ style={style}
60
87
  {...props}
61
88
  >
62
89
  {children}
package/client/global.css CHANGED
@@ -1780,6 +1780,34 @@ body {
1780
1780
  background-color: var(--color-blue-200, #cbe0f1);
1781
1781
  }
1782
1782
 
1783
+ /* Tag Code Variant - for code badges like "C1", "A3" */
1784
+ .tag-code {
1785
+ display: flex;
1786
+ align-items: center;
1787
+ justify-content: center;
1788
+ height: 2rem;
1789
+ width: 3rem;
1790
+ border-radius: calc(var(--radius-sm, 6px));
1791
+ flex-shrink: 0;
1792
+ border: 1.5px solid currentColor;
1793
+ padding-left: 1px;
1794
+ padding-right: 1px;
1795
+ background: transparent;
1796
+ cursor: pointer;
1797
+ }
1798
+
1799
+ .tag-code:focus-visible {
1800
+ outline: none;
1801
+ box-shadow:
1802
+ 0 0 0 2px hsl(var(--ring)),
1803
+ 0 0 0 4px var(--color-white, #fff);
1804
+ }
1805
+
1806
+ .tag-code:disabled {
1807
+ opacity: 0.5;
1808
+ cursor: not-allowed;
1809
+ }
1810
+
1783
1811
  /* Toggle/Segmented Control Styles */
1784
1812
  .toggle {
1785
1813
  display: inline-flex;