@pure-ds/core 0.3.19 → 0.4.1

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.
@@ -546,7 +546,7 @@ export class SchemaForm extends LitElement {
546
546
  }
547
547
 
548
548
  const fieldsetClass =
549
- layoutClasses.length > 0 ? layoutClasses.join(" ") : undefined;
549
+ layoutClasses.length > 0 ? layoutClasses.join(" ") : "stack-sm";
550
550
 
551
551
  // Render basic fieldset
552
552
  const fieldsetContent = html`
@@ -1140,7 +1140,7 @@ export class SchemaForm extends LitElement {
1140
1140
  : node.widgetKey === "checkbox-group"
1141
1141
  ? "group"
1142
1142
  : undefined;
1143
- const fieldsetClass = ui?.["ui:class"];
1143
+ const fieldsetClass = ui?.["ui:class"] ?? "stack-sm";
1144
1144
  return html`
1145
1145
  <fieldset
1146
1146
  data-path=${path}
@@ -1,10 +1,20 @@
1
1
  /**
2
+ * A tab panel used as a child of `<pds-tabstrip>`. Each panel becomes a tab
3
+ * with its button label derived from the `label` attribute.
4
+ *
2
5
  * @element pds-tabpanel
3
6
  *
4
- * @attr {string} label - Label for the tab button
7
+ * @attr {string} label - Label text displayed on the tab button
5
8
  * @attr {string} id - Unique identifier for the panel (auto-generated if not provided)
6
9
  *
7
- * @slot - Content of the tab panel
10
+ * @slot - Content displayed when this tab is active
11
+ *
12
+ * @example
13
+ * ```html
14
+ * <pds-tabpanel label="Settings">
15
+ * <p>Settings content here.</p>
16
+ * </pds-tabpanel>
17
+ * ```
8
18
  */
9
19
  class TabPanel extends HTMLElement {
10
20
  connectedCallback() {
@@ -36,18 +46,39 @@ class TabPanel extends HTMLElement {
36
46
  customElements.define("pds-tabpanel", TabPanel);
37
47
 
38
48
  /**
39
- * Tab navigation component that pairs anchors with `pds-tabpanel` children.
49
+ * Tab navigation component that displays content in switchable panels.
50
+ *
51
+ * Use `<pds-tabpanel>` children with a `label` attribute to define each tab.
52
+ * The component auto-generates navigation buttons and handles URL hash synchronization.
40
53
  *
41
54
  * @element pds-tabstrip
55
+ *
42
56
  * @fires tabchange - Fired when the active tab changes. Detail: `{ oldTab: string|null, newTab: string }`
43
57
  *
44
- * @attr {string} label - Accessible label announced for the tablist
58
+ * @attr {string} label - Accessible label announced for the tablist (default: "Tabs")
45
59
  * @attr {string} selected - Identifier of the currently active panel (synced with the location hash)
46
60
  *
47
- * @slot - Collection of `pds-tabpanel` nodes representing individual tab panels
61
+ * @slot - One or more `<pds-tabpanel>` elements, each with a `label` attribute
48
62
  *
49
63
  * @csspart tabs - Navigation container comprising the clickable tab buttons
50
64
  * @cssprop --color-accent-400 - Color of the active tab indicator underline
65
+ *
66
+ * @example
67
+ * ```html
68
+ * <pds-tabstrip label="Account settings">
69
+ * <pds-tabpanel label="General">
70
+ * <p>General settings content here.</p>
71
+ * </pds-tabpanel>
72
+ *
73
+ * <pds-tabpanel label="Security">
74
+ * <p>Security settings content here.</p>
75
+ * </pds-tabpanel>
76
+ *
77
+ * <pds-tabpanel label="Notifications">
78
+ * <p>Notification preferences here.</p>
79
+ * </pds-tabpanel>
80
+ * </pds-tabstrip>
81
+ * ```
51
82
  */
52
83
  class TabStrip extends HTMLElement {
53
84
  #shadow = this.attachShadow({ mode: "open" });
@@ -1432,11 +1432,11 @@
1432
1432
  "declarations": [
1433
1433
  {
1434
1434
  "kind": "class",
1435
- "description": "",
1435
+ "description": "A tab panel used as a child of `<pds-tabstrip>`. Each panel becomes a tab\nwith its button label derived from the `label` attribute.",
1436
1436
  "name": "TabPanel",
1437
1437
  "slots": [
1438
1438
  {
1439
- "description": "Content of the tab panel",
1439
+ "description": "Content displayed when this tab is active",
1440
1440
  "name": ""
1441
1441
  }
1442
1442
  ],
@@ -1458,7 +1458,7 @@
1458
1458
  "type": {
1459
1459
  "text": "string"
1460
1460
  },
1461
- "description": "Label for the tab button",
1461
+ "description": "Label text displayed on the tab button",
1462
1462
  "name": "label"
1463
1463
  },
1464
1464
  {
@@ -1477,7 +1477,7 @@
1477
1477
  },
1478
1478
  {
1479
1479
  "kind": "class",
1480
- "description": "Tab navigation component that pairs anchors with `pds-tabpanel` children.",
1480
+ "description": "Tab navigation component that displays content in switchable panels.\n\nUse `<pds-tabpanel>` children with a `label` attribute to define each tab.\nThe component auto-generates navigation buttons and handles URL hash synchronization.",
1481
1481
  "name": "TabStrip",
1482
1482
  "cssProperties": [
1483
1483
  {
@@ -1493,7 +1493,7 @@
1493
1493
  ],
1494
1494
  "slots": [
1495
1495
  {
1496
- "description": "Collection of `pds-tabpanel` nodes representing individual tab panels",
1496
+ "description": "One or more `<pds-tabpanel>` elements, each with a `label` attribute",
1497
1497
  "name": ""
1498
1498
  }
1499
1499
  ],
@@ -1518,7 +1518,7 @@
1518
1518
  "type": {
1519
1519
  "text": "string"
1520
1520
  },
1521
- "description": "Accessible label announced for the tablist",
1521
+ "description": "Accessible label announced for the tablist (default: \"Tabs\")",
1522
1522
  "name": "label"
1523
1523
  },
1524
1524
  {
@@ -1853,7 +1853,7 @@
1853
1853
  "dataAttributes": [],
1854
1854
  "metadata": {
1855
1855
  "generator": "PDS CSS Data Generator",
1856
- "generatedAt": "2025-12-19T08:07:14.665Z",
1856
+ "generatedAt": "2025-12-19T15:38:18.395Z",
1857
1857
  "totalProperties": 165,
1858
1858
  "totalClasses": 91,
1859
1859
  "totalAttributes": 0
@@ -749,11 +749,11 @@
749
749
  },
750
750
  {
751
751
  "name": "pds-tabpanel",
752
- "description": "TabPanel component",
752
+ "description": "A tab panel used as a child of `<pds-tabstrip>`. Each panel becomes a tab\nwith its button label derived from the `label` attribute.",
753
753
  "attributes": [
754
754
  {
755
755
  "name": "label",
756
- "description": "Label for the tab button"
756
+ "description": "Label text displayed on the tab button"
757
757
  },
758
758
  {
759
759
  "name": "id",
@@ -763,11 +763,11 @@
763
763
  },
764
764
  {
765
765
  "name": "pds-tabstrip",
766
- "description": "Tab navigation component that pairs anchors with `pds-tabpanel` children.",
766
+ "description": "Tab navigation component that displays content in switchable panels.\n\nUse `<pds-tabpanel>` children with a `label` attribute to define each tab.\nThe component auto-generates navigation buttons and handles URL hash synchronization.",
767
767
  "attributes": [
768
768
  {
769
769
  "name": "label",
770
- "description": "Accessible label announced for the tablist"
770
+ "description": "Accessible label announced for the tablist (default: \"Tabs\")"
771
771
  },
772
772
  {
773
773
  "name": "selected",
@@ -224,10 +224,8 @@ function enhanceRequired(elem) {
224
224
 
225
225
  const form = elem.closest("form");
226
226
  if (form && !form.querySelector(".required-legend")) {
227
- const legend = document.createElement("div");
228
- legend.classList.add("required-legend", "pill", "pill-outline");
229
- legend.style.fontSize = "0.9em";
230
- legend.style.marginBottom = "8px";
227
+ const legend = document.createElement("small");
228
+ legend.classList.add("required-legend");
231
229
  legend.textContent = "* Required fields";
232
230
  form.insertBefore(
233
231
  legend,
@@ -344,7 +342,7 @@ const enhancerRunners = new Map([
344
342
  ["nav[data-dropdown]", enhanceDropdown],
345
343
  ["label[data-toggle]", enhanceToggle],
346
344
  ['input[type="range"]', enhanceRange],
347
- ["form [required]", enhanceRequired],
345
+ ["form[data-required] [required]", enhanceRequired],
348
346
  ["fieldset[role=group][data-open]", enhanceOpenGroup],
349
347
  ["button, a[class*='btn-']", enhanceButtonWorking],
350
348
  ]);
@@ -1645,6 +1645,36 @@ html[data-theme="dark"] .liquid-glass {
1645
1645
  height: 0;
1646
1646
  }
1647
1647
 
1648
+ /* Labeled horizontal rule: <hr data-content="OR"> */
1649
+ :where(hr[data-content]) {
1650
+ position: relative;
1651
+ border: none;
1652
+ text-align: center;
1653
+ height: auto;
1654
+ overflow: visible;
1655
+
1656
+ &::before {
1657
+ content: "";
1658
+ position: absolute;
1659
+ left: 0;
1660
+ top: 50%;
1661
+ width: 100%;
1662
+ height: 1px;
1663
+ background: linear-gradient(to right, transparent, var(--color-border), transparent);
1664
+ }
1665
+
1666
+ &::after {
1667
+ content: attr(data-content);
1668
+ position: relative;
1669
+ display: inline-block;
1670
+ padding: 0 var(--spacing-3);
1671
+ background-color: var(--color-surface-base);
1672
+ color: var(--color-text-muted);
1673
+ font-size: var(--font-size-sm);
1674
+ line-height: var(--font-line-height-normal);
1675
+ }
1676
+ }
1677
+
1648
1678
  :where(dl) {
1649
1679
  margin: 0 0 var(--spacing-4) 0;
1650
1680
  }
@@ -1801,11 +1831,7 @@ form {
1801
1831
  }
1802
1832
 
1803
1833
  fieldset {
1804
- margin: 0 0 var(--spacing-${
1805
- Number.isFinite(Math.round((gapValue * sectionSpacingValue) / 4))
1806
- ? Math.round((gapValue * sectionSpacingValue) / 4)
1807
- : 1
1808
- }) 0;
1834
+ margin: 0
1809
1835
  padding: var(--spacing-5);
1810
1836
  width: 100%;
1811
1837
  background-color: color-mix(in oklab, var(--color-surface-subtle) 50%, transparent 50%);
@@ -1880,7 +1906,6 @@ fieldset fieldset fieldset > legend { font-size: var(--font-size-sm); }
1880
1906
 
1881
1907
  label {
1882
1908
  display: block;
1883
- margin-bottom: var(--spacing-3);
1884
1909
  font-weight: var(--font-weight-medium);
1885
1910
  color: var(--color-text-primary);
1886
1911
  font-size: var(--font-size-sm);
@@ -2600,16 +2625,7 @@ a.btn-working {
2600
2625
  border: ${borderWidth}px solid var(--color-border);
2601
2626
  border-radius: var(--radius-md);
2602
2627
  background-color: var(--color-surface-base);
2603
-
2604
- fieldset {
2605
- background-color: transparent;
2606
- margin-bottom: var(--spacing-3);
2607
-
2608
- &:last-of-type {
2609
- margin-bottom: 0;
2610
- }
2611
- }
2612
-
2628
+
2613
2629
  .array-controls {
2614
2630
  padding-top: var(--spacing-3);
2615
2631
  border-top: ${borderWidth}px solid var(--color-border);
@@ -3063,8 +3079,7 @@ dialog {
3063
3079
  overlay 0.2s ease allow-discrete,
3064
3080
  display 0.2s ease allow-discrete;
3065
3081
 
3066
- /* Overflow handling */
3067
- overflow: hidden;
3082
+
3068
3083
  }
3069
3084
 
3070
3085
  /* Open state */
@@ -3166,7 +3181,7 @@ dialog {
3166
3181
  form > article,
3167
3182
  .dialog-body {
3168
3183
  flex: 1;
3169
- padding: var(--spacing-6);
3184
+ padding: var(--spacing-3) var(--spacing-6);
3170
3185
  overflow-y: auto;
3171
3186
  overflow-x: hidden;
3172
3187
  }
@@ -3365,40 +3380,40 @@ pds-tabstrip {
3365
3380
  ::-webkit-scrollbar {
3366
3381
  width: 12px;
3367
3382
  height: 12px;
3383
+ }
3384
+
3385
+ ::-webkit-scrollbar-track {
3386
+ background: transparent;
3387
+ }
3388
+
3389
+ ::-webkit-scrollbar-thumb {
3390
+ background: var(--color-secondary-300);
3391
+ border-radius: var(--radius-full);
3392
+ border: 3px solid transparent;
3393
+ background-clip: padding-box;
3394
+ transition: background-color var(--transition-fast);
3368
3395
 
3369
- &-track {
3370
- background: transparent;
3396
+ &:hover {
3397
+ background: var(--color-secondary-400);
3398
+ border: 2px solid transparent;
3399
+ background-clip: padding-box;
3371
3400
  }
3372
3401
 
3373
- &-thumb {
3374
- background: var(--color-secondary-300);
3375
- border-radius: var(--radius-full);
3376
- border: 3px solid transparent;
3402
+ &:active {
3403
+ background: var(--color-secondary-500);
3404
+ border: 2px solid transparent;
3377
3405
  background-clip: padding-box;
3378
- transition: background-color var(--transition-fast);
3406
+ }
3407
+
3408
+ @media (prefers-color-scheme: dark) {
3409
+ background: var(--color-secondary-600);
3379
3410
 
3380
3411
  &:hover {
3381
- background: var(--color-secondary-400);
3382
- border: 2px solid transparent;
3383
- background-clip: padding-box;
3384
- }
3385
-
3386
- &:active {
3387
3412
  background: var(--color-secondary-500);
3388
- border: 2px solid transparent;
3389
- background-clip: padding-box;
3390
3413
  }
3391
3414
 
3392
- @media (prefers-color-scheme: dark) {
3393
- background: var(--color-secondary-600);
3394
-
3395
- &:hover {
3396
- background: var(--color-secondary-500);
3397
- }
3398
-
3399
- &:active {
3400
- background: var(--color-secondary-400);
3401
- }
3415
+ &:active {
3416
+ background: var(--color-secondary-400);
3402
3417
  }
3403
3418
  }
3404
3419
  }
@@ -3843,6 +3858,11 @@ nav[data-dropdown] {
3843
3858
  flex-direction: row;
3844
3859
  }
3845
3860
 
3861
+ /* Flex grow - fill remaining space */
3862
+ .grow {
3863
+ flex: 1 1 0%;
3864
+ }
3865
+
3846
3866
  /* Flex alignment */
3847
3867
  .items-start { align-items: flex-start; }
3848
3868
  .items-center { align-items: center; }
@@ -3857,6 +3877,34 @@ nav[data-dropdown] {
3857
3877
  .justify-around { justify-content: space-around; }
3858
3878
  .justify-evenly { justify-content: space-evenly; }
3859
3879
 
3880
+ /* Text alignment utilities */
3881
+ .text-left { text-align: left; }
3882
+ .text-center { text-align: center; }
3883
+ .text-right { text-align: right; }
3884
+
3885
+ /* Text overflow utility */
3886
+ .truncate {
3887
+ overflow: hidden;
3888
+ text-overflow: ellipsis;
3889
+ white-space: nowrap;
3890
+ }
3891
+
3892
+ /* Max-width utilities (semantic breakpoints) */
3893
+ .max-w-sm { max-width: 400px; }
3894
+ .max-w-md { max-width: 600px; }
3895
+ .max-w-lg { max-width: 800px; }
3896
+ .max-w-xl { max-width: 1200px; }
3897
+
3898
+ /* Stack utilities - vertical rhythm for stacked elements */
3899
+ .stack-sm { display: flex; flex-direction: column; gap: var(--spacing-2); }
3900
+ .stack-md { display: flex; flex-direction: column; gap: var(--spacing-4); }
3901
+ .stack-lg { display: flex; flex-direction: column; gap: var(--spacing-6); }
3902
+ .stack-xl { display: flex; flex-direction: column; gap: var(--spacing-8); }
3903
+
3904
+ /* Section spacing - for major content blocks */
3905
+ .section { padding-block: var(--spacing-8); }
3906
+ .section-lg { padding-block: var(--spacing-12); }
3907
+
3860
3908
  /* Responsive helpers */
3861
3909
  @media (max-width: ${breakpoints.md - 1}px) {
3862
3910
  .mobile-stack { flex-direction: column; }
@@ -3883,29 +3931,29 @@ nav[data-dropdown] {
3883
3931
  opacity: var(--backdrop-opacity, 1);
3884
3932
  pointer-events: auto;
3885
3933
  }
3934
+ }
3886
3935
 
3887
- /* Backdrop variants */
3888
- &-light {
3889
- --backdrop-bg: linear-gradient(135deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.2));
3890
- --backdrop-brightness: 1.1;
3891
- }
3936
+ /* Backdrop variants */
3937
+ .backdrop-light {
3938
+ --backdrop-bg: linear-gradient(135deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.2));
3939
+ --backdrop-brightness: 1.1;
3940
+ }
3892
3941
 
3893
- &-dark {
3894
- --backdrop-bg: linear-gradient(135deg, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.5));
3895
- --backdrop-brightness: 0.6;
3896
- }
3942
+ .backdrop-dark {
3943
+ --backdrop-bg: linear-gradient(135deg, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.5));
3944
+ --backdrop-brightness: 0.6;
3945
+ }
3897
3946
 
3898
- &-blur-sm {
3899
- --backdrop-blur: 5px;
3900
- }
3947
+ .backdrop-blur-sm {
3948
+ --backdrop-blur: 5px;
3949
+ }
3901
3950
 
3902
- &-blur-md {
3903
- --backdrop-blur: 10px;
3904
- }
3951
+ .backdrop-blur-md {
3952
+ --backdrop-blur: 10px;
3953
+ }
3905
3954
 
3906
- &-blur-lg {
3907
- --backdrop-blur: 20px;
3908
- }
3955
+ .backdrop-blur-lg {
3956
+ --backdrop-blur: 20px;
3909
3957
  }
3910
3958
  `
3911
3959
  );
@@ -4640,23 +4688,23 @@ ${this.#generateTableStyles()}
4640
4688
  background: var(--color-surface-base);
4641
4689
  border-radius: var(--radius-md);
4642
4690
  padding: var(--spacing-4);
4691
+ }
4643
4692
 
4644
- &-elevated {
4645
- background: var(--color-surface-elevated);
4646
- box-shadow: var(--shadow-md);
4647
- }
4693
+ .card-elevated {
4694
+ background: var(--color-surface-elevated);
4695
+ box-shadow: var(--shadow-md);
4696
+ }
4648
4697
 
4649
- &-outlined,
4650
- &-basic {
4651
- background: var(--color-surface-base);
4652
- border: 1px solid var(--color-border);
4653
- }
4698
+ .card-outlined,
4699
+ .card-basic {
4700
+ background: var(--color-surface-base);
4701
+ border: 1px solid var(--color-border);
4702
+ }
4654
4703
 
4655
- &-interactive:hover {
4656
- transform: translateY(-2px);
4657
- box-shadow: var(--shadow-lg);
4658
- transition: transform var(--transition-fast), box-shadow var(--transition-fast);
4659
- }
4704
+ .card-interactive:hover {
4705
+ transform: translateY(-2px);
4706
+ box-shadow: var(--shadow-lg);
4707
+ transition: transform var(--transition-fast), box-shadow var(--transition-fast);
4660
4708
  }
4661
4709
 
4662
4710
  ${this.#generateScrollbarStyles()}
@@ -4744,6 +4792,34 @@ ${this.#generateBorderGradientUtilities()}
4744
4792
  pds-icon {
4745
4793
  color: var(--surface-inverse-icon);
4746
4794
  }
4795
+
4796
+ /* Default and secondary buttons on inverse - semi-transparent glass effect */
4797
+ & button:not(.btn-primary):not(.btn-outline):not(.btn-danger):not(.btn-success):not(.btn-warning),
4798
+ & .btn-secondary {
4799
+ background-color: rgba(255, 255, 255, 0.12);
4800
+ color: var(--surface-inverse-text);
4801
+ border-color: rgba(255, 255, 255, 0.25);
4802
+
4803
+ &:hover {
4804
+ background-color: rgba(255, 255, 255, 0.2);
4805
+ }
4806
+
4807
+ &:active {
4808
+ background-color: rgba(255, 255, 255, 0.28);
4809
+ }
4810
+ }
4811
+
4812
+ /* Ensure btn-primary stays vibrant on inverse */
4813
+ & .btn-primary {
4814
+ background-color: var(--color-primary-500);
4815
+ border-color: var(--color-primary-500);
4816
+ color: var(--color-primary-contrast, #ffffff);
4817
+
4818
+ &:hover {
4819
+ background-color: var(--color-primary-400);
4820
+ border-color: var(--color-primary-400);
4821
+ }
4822
+ }
4747
4823
  }
4748
4824
 
4749
4825
  /* Shadow utilities */