@fpkit/acss 6.3.0 → 6.4.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.
- package/libs/components/alert/alert.css +1 -1
- package/libs/components/alert/alert.css.map +1 -1
- package/libs/components/alert/alert.min.css +2 -2
- package/libs/components/buttons/icon-button.css +1 -1
- package/libs/components/buttons/icon-button.css.map +1 -1
- package/libs/components/buttons/icon-button.min.css +2 -2
- package/libs/components/dialog/dialog.css +1 -1
- package/libs/components/dialog/dialog.css.map +1 -1
- package/libs/components/dialog/dialog.min.css +2 -2
- package/libs/components/icons/icon.d.cts +1 -1
- package/libs/components/icons/icon.d.ts +1 -1
- package/libs/{icons-2c09535c.d.ts → icons-48788561.d.ts} +32 -32
- package/libs/icons.d.cts +1 -1
- package/libs/icons.d.ts +1 -1
- package/libs/index.cjs.map +1 -1
- package/libs/index.css +1 -1
- package/libs/index.css.map +1 -1
- package/libs/index.d.cts +11 -8
- package/libs/index.d.ts +11 -8
- package/libs/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/alert/alert.scss +0 -13
- package/src/components/buttons/icon-button.mdx +204 -0
- package/src/components/buttons/icon-button.scss +64 -26
- package/src/components/buttons/icon-button.tsx +9 -6
- package/src/components/dialog/dialog-modal.stories.tsx +71 -0
- package/src/components/dialog/dialog-modal.tsx +29 -3
- package/src/components/dialog/dialog.scss +1 -0
- package/src/components/dialog/dialog.test.tsx +119 -0
- package/src/components/dialog/dialog.types.ts +8 -1
- package/src/sass/utilities/_display.scss +156 -0
- package/src/sass/utilities/_index.scss +3 -0
- package/src/sass/utilities/display.mdx +203 -0
- package/src/sass/utilities/display.stories.tsx +141 -0
- package/src/styles/alert/alert.css +0 -13
- package/src/styles/alert/alert.css.map +1 -1
- package/src/styles/buttons/icon-button.css +55 -16
- package/src/styles/buttons/icon-button.css.map +1 -1
- package/src/styles/dialog/dialog.css +1 -0
- package/src/styles/dialog/dialog.css.map +1 -1
- package/src/styles/index.css +136 -13
- package/src/styles/index.css.map +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CSSProperties, ReactNode } from "react";
|
|
1
|
+
import { CSSProperties, ReactElement, ReactNode } from "react";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Base properties shared by all dialog variants.
|
|
@@ -70,6 +70,7 @@ export interface DialogProps extends BaseDialogProps {
|
|
|
70
70
|
* @property {string} [btnLabel="Open Dialog"] - Text label for the trigger button
|
|
71
71
|
* @property {"sm" | "md" | "lg"} [btnSize="sm"] - Size variant for the trigger button
|
|
72
72
|
* @property {() => void} [btnOnClick] - Callback fired when trigger button is clicked (before opening)
|
|
73
|
+
* @property {ReactElement} [icon] - Optional icon element. When provided, renders IconButton instead of Button as trigger.
|
|
73
74
|
*/
|
|
74
75
|
export interface DialogModalProps extends BaseDialogProps {
|
|
75
76
|
/** If true, renders as non-modal inline alert using dialog.show() */
|
|
@@ -92,6 +93,12 @@ export interface DialogModalProps extends BaseDialogProps {
|
|
|
92
93
|
btnOnClick?: () => void;
|
|
93
94
|
/** Additional props to pass to the trigger button component */
|
|
94
95
|
btnProps?: Record<string, unknown>;
|
|
96
|
+
/**
|
|
97
|
+
* Optional icon element. When provided, renders an IconButton instead of a regular Button as the trigger.
|
|
98
|
+
* `btnLabel` serves as both `aria-label` and the visible label text (shown at desktop widths via IconButton's responsive label).
|
|
99
|
+
* Note: `aria-labelledby` cannot be passed via `btnProps` when icon is set — use `btnLabel` instead.
|
|
100
|
+
*/
|
|
101
|
+
icon?: ReactElement;
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
/**
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
DISPLAY UTILITIES - Visibility and Display Control
|
|
3
|
+
============================================================================
|
|
4
|
+
|
|
5
|
+
Utility classes for showing/hiding elements at any breakpoint.
|
|
6
|
+
All utilities use !important to win specificity over component styles.
|
|
7
|
+
|
|
8
|
+
Breakpoints (matching flex.scss):
|
|
9
|
+
- sm: 30rem (480px)
|
|
10
|
+
- md: 48rem (768px)
|
|
11
|
+
- lg: 62rem (992px)
|
|
12
|
+
- xl: 80rem (1280px)
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
<div class="hide">Always hidden</div>
|
|
16
|
+
<div class="hide md:show">Hidden on mobile, shown at md+</div>
|
|
17
|
+
<span class="sr-only">Screen reader only</span>
|
|
18
|
+
<a href="#main" class="sr-only-focusable">Skip link</a>
|
|
19
|
+
<nav class="print:hide">Not printed</nav>
|
|
20
|
+
============================================================================ */
|
|
21
|
+
|
|
22
|
+
/* Base utilities (no breakpoint)
|
|
23
|
+
========================================================================== */
|
|
24
|
+
|
|
25
|
+
/** Removes element from layout and accessibility tree */
|
|
26
|
+
.hide {
|
|
27
|
+
display: none !important;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Restores browser default display per element type */
|
|
31
|
+
.show {
|
|
32
|
+
display: revert !important;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Hides element visually but preserves layout space */
|
|
36
|
+
.invisible {
|
|
37
|
+
visibility: hidden !important;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Accessibility utilities
|
|
41
|
+
========================================================================== */
|
|
42
|
+
|
|
43
|
+
/** Visually hidden but present in the accessibility tree */
|
|
44
|
+
.sr-only {
|
|
45
|
+
position: absolute;
|
|
46
|
+
width: 1px;
|
|
47
|
+
height: 1px;
|
|
48
|
+
padding: 0;
|
|
49
|
+
margin: -1px;
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
clip: rect(0, 0, 0, 0);
|
|
52
|
+
white-space: nowrap;
|
|
53
|
+
border-width: 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** sr-only that becomes visible on focus — for skip links (WCAG 2.4.1) */
|
|
57
|
+
.sr-only-focusable {
|
|
58
|
+
position: absolute;
|
|
59
|
+
width: 1px;
|
|
60
|
+
height: 1px;
|
|
61
|
+
padding: 0;
|
|
62
|
+
margin: -1px;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
clip: rect(0, 0, 0, 0);
|
|
65
|
+
white-space: nowrap;
|
|
66
|
+
border-width: 0;
|
|
67
|
+
|
|
68
|
+
&:focus,
|
|
69
|
+
&:focus-within {
|
|
70
|
+
position: static;
|
|
71
|
+
width: auto;
|
|
72
|
+
height: auto;
|
|
73
|
+
padding: 0;
|
|
74
|
+
margin: 0;
|
|
75
|
+
overflow: visible;
|
|
76
|
+
clip: auto;
|
|
77
|
+
white-space: normal;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Print variant
|
|
82
|
+
========================================================================== */
|
|
83
|
+
|
|
84
|
+
@media print {
|
|
85
|
+
.print\:hide {
|
|
86
|
+
display: none !important;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* Responsive variants — sm (30rem / 480px)
|
|
91
|
+
========================================================================== */
|
|
92
|
+
|
|
93
|
+
@media (width >= 30rem) {
|
|
94
|
+
.sm\:hide {
|
|
95
|
+
display: none !important;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.sm\:show {
|
|
99
|
+
display: revert !important;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.sm\:invisible {
|
|
103
|
+
visibility: hidden !important;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Responsive variants — md (48rem / 768px)
|
|
108
|
+
========================================================================== */
|
|
109
|
+
|
|
110
|
+
@media (width >= 48rem) {
|
|
111
|
+
.md\:hide {
|
|
112
|
+
display: none !important;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.md\:show {
|
|
116
|
+
display: revert !important;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.md\:invisible {
|
|
120
|
+
visibility: hidden !important;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Responsive variants — lg (62rem / 992px)
|
|
125
|
+
========================================================================== */
|
|
126
|
+
|
|
127
|
+
@media (width >= 62rem) {
|
|
128
|
+
.lg\:hide {
|
|
129
|
+
display: none !important;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.lg\:show {
|
|
133
|
+
display: revert !important;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.lg\:invisible {
|
|
137
|
+
visibility: hidden !important;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* Responsive variants — xl (80rem / 1280px)
|
|
142
|
+
========================================================================== */
|
|
143
|
+
|
|
144
|
+
@media (width >= 80rem) {
|
|
145
|
+
.xl\:hide {
|
|
146
|
+
display: none !important;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.xl\:show {
|
|
150
|
+
display: revert !important;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.xl\:invisible {
|
|
154
|
+
visibility: hidden !important;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { Meta } from "@storybook/addon-docs/blocks";
|
|
2
|
+
|
|
3
|
+
<Meta title="FP.React Components/Utilities/Display/Readme" />
|
|
4
|
+
|
|
5
|
+
# Display & Visibility Utilities
|
|
6
|
+
|
|
7
|
+
## Summary
|
|
8
|
+
|
|
9
|
+
`_display.scss` provides a set of atomic utility classes for controlling element
|
|
10
|
+
visibility and display at any breakpoint. These utilities use `!important` to
|
|
11
|
+
override component-level specificity, making them reliable for layout corrections
|
|
12
|
+
and responsive show/hide patterns.
|
|
13
|
+
|
|
14
|
+
The utility layer covers four concerns:
|
|
15
|
+
|
|
16
|
+
- **Layout hiding** — remove an element from the layout and accessibility tree
|
|
17
|
+
- **Visual hiding** — hide visually while preserving layout space
|
|
18
|
+
- **Accessibility hiding** — hide visually but keep the element in the a11y tree
|
|
19
|
+
- **Print control** — suppress elements when printing
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Base Class Reference
|
|
24
|
+
|
|
25
|
+
| Class | CSS Applied | Effect |
|
|
26
|
+
| ------------------ | ------------------------------------ | --------------------------------------------------- |
|
|
27
|
+
| `.hide` | `display: none !important` | Removed from layout and accessibility tree |
|
|
28
|
+
| `.show` | `display: revert !important` | Restores browser default display for the element |
|
|
29
|
+
| `.invisible` | `visibility: hidden !important` | Visually hidden; layout space is preserved (ghost) |
|
|
30
|
+
| `.sr-only` | Visually-hidden technique (absolute) | Hidden visually; announced by screen readers |
|
|
31
|
+
| `.sr-only-focusable` | sr-only + visible on `:focus` | Skip-link pattern — visible on keyboard focus |
|
|
32
|
+
| `.print:hide` | `display: none !important` in print | Hidden when printing; visible on screen |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Responsive Class Reference
|
|
37
|
+
|
|
38
|
+
Each breakpoint prefix applies its utility at that viewport width **and above**
|
|
39
|
+
(mobile-first). Combine with a base class to create responsive patterns.
|
|
40
|
+
|
|
41
|
+
| Class | Breakpoint | CSS Applied |
|
|
42
|
+
| -------------- | --------------- | ------------------------------- |
|
|
43
|
+
| `.sm:hide` | `>= 30rem` | `display: none !important` |
|
|
44
|
+
| `.sm:show` | `>= 30rem` | `display: revert !important` |
|
|
45
|
+
| `.sm:invisible`| `>= 30rem` | `visibility: hidden !important` |
|
|
46
|
+
| `.md:hide` | `>= 48rem` | `display: none !important` |
|
|
47
|
+
| `.md:show` | `>= 48rem` | `display: revert !important` |
|
|
48
|
+
| `.md:invisible`| `>= 48rem` | `visibility: hidden !important` |
|
|
49
|
+
| `.lg:hide` | `>= 62rem` | `display: none !important` |
|
|
50
|
+
| `.lg:show` | `>= 62rem` | `display: revert !important` |
|
|
51
|
+
| `.lg:invisible`| `>= 62rem` | `visibility: hidden !important` |
|
|
52
|
+
| `.xl:hide` | `>= 80rem` | `display: none !important` |
|
|
53
|
+
| `.xl:show` | `>= 80rem` | `display: revert !important` |
|
|
54
|
+
| `.xl:invisible`| `>= 80rem` | `visibility: hidden !important` |
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Breakpoint Reference
|
|
59
|
+
|
|
60
|
+
Breakpoints match the flex layout system (`flex.scss`).
|
|
61
|
+
|
|
62
|
+
| Token | rem | px |
|
|
63
|
+
| ----- | ------ | ------ |
|
|
64
|
+
| `sm` | 30rem | 480px |
|
|
65
|
+
| `md` | 48rem | 768px |
|
|
66
|
+
| `lg` | 62rem | 992px |
|
|
67
|
+
| `xl` | 80rem | 1280px |
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Responsive Patterns
|
|
72
|
+
|
|
73
|
+
### Mobile-only element (hide at `md` and above)
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<div class="md:hide">Only visible on mobile</div>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Desktop-only element (hide by default, show at `md` and above)
|
|
80
|
+
|
|
81
|
+
```html
|
|
82
|
+
<div class="hide md:show">Only visible at 768px+</div>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Large screen only
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<nav class="hide lg:show">Desktop navigation</nav>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Invisible placeholder on wide screens
|
|
92
|
+
|
|
93
|
+
Preserve the layout gap while hiding the element visually at `xl`:
|
|
94
|
+
|
|
95
|
+
```html
|
|
96
|
+
<div class="xl:invisible">Takes up space but invisible at 1280px+</div>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Stacked responsive rules
|
|
100
|
+
|
|
101
|
+
Classes compose additively. This element hides at `sm` but reappears at `lg`:
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<aside class="sm:hide lg:show">Sidebar</aside>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
> **Note:** Because each breakpoint class uses `!important`, later (wider)
|
|
108
|
+
> breakpoints win over earlier ones when viewport conditions overlap.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Accessibility Utilities
|
|
113
|
+
|
|
114
|
+
### `.sr-only` — Screen-reader-only content
|
|
115
|
+
|
|
116
|
+
Hides an element visually using the established visually-hidden technique, while
|
|
117
|
+
keeping it present in the accessibility tree so screen readers still announce it.
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
<span class="sr-only">Current page: Home</span>
|
|
121
|
+
|
|
122
|
+
<button>
|
|
123
|
+
<svg aria-hidden="true"><!-- icon --></svg>
|
|
124
|
+
<span class="sr-only">Close dialog</span>
|
|
125
|
+
</button>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
WCAG reference: SC 1.3.1 Info and Relationships — meaningful content must be
|
|
129
|
+
programmatically determinable.
|
|
130
|
+
|
|
131
|
+
### `.sr-only-focusable` — Skip links (WCAG 2.4.1)
|
|
132
|
+
|
|
133
|
+
Behaves like `.sr-only` until the element receives keyboard focus, at which
|
|
134
|
+
point it becomes fully visible. The standard pattern for skip-navigation links.
|
|
135
|
+
|
|
136
|
+
```html
|
|
137
|
+
<a href="#main-content" class="sr-only-focusable">Skip to main content</a>
|
|
138
|
+
|
|
139
|
+
<main id="main-content">
|
|
140
|
+
<!-- page content -->
|
|
141
|
+
</main>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
On focus, the element snaps back to `position: static`, `width: auto`,
|
|
145
|
+
`height: auto` — appearing at its natural position in the document flow.
|
|
146
|
+
|
|
147
|
+
WCAG reference: SC 2.4.1 Bypass Blocks — a mechanism to skip repeated
|
|
148
|
+
navigation is required for keyboard users.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Print Utility
|
|
153
|
+
|
|
154
|
+
### `.print:hide` — Hide in print
|
|
155
|
+
|
|
156
|
+
The element renders normally on screen but is suppressed via
|
|
157
|
+
`@media print { display: none !important }`. Use for navigation, ads,
|
|
158
|
+
sidebars, or any chrome that clutters a printed page.
|
|
159
|
+
|
|
160
|
+
```html
|
|
161
|
+
<nav class="print:hide">Main navigation</nav>
|
|
162
|
+
<aside class="print:hide">Related articles sidebar</aside>
|
|
163
|
+
|
|
164
|
+
<article>
|
|
165
|
+
<!-- This content prints normally -->
|
|
166
|
+
</article>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Best Practices
|
|
172
|
+
|
|
173
|
+
### Prefer semantic HTML first
|
|
174
|
+
|
|
175
|
+
Use proper `hidden` attribute or ARIA before reaching for `.hide`:
|
|
176
|
+
|
|
177
|
+
```html
|
|
178
|
+
<!-- Native hidden (preferred for static content) -->
|
|
179
|
+
<div hidden>Not visible, not in a11y tree</div>
|
|
180
|
+
|
|
181
|
+
<!-- .hide for dynamic responsive cases or JS-toggled visibility -->
|
|
182
|
+
<div class="hide md:show">Conditionally visible</div>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Use `.sr-only` instead of `aria-hidden` for meaningful content
|
|
186
|
+
|
|
187
|
+
`aria-hidden="true"` removes content from the a11y tree entirely. `.sr-only`
|
|
188
|
+
hides it visually but keeps it accessible. Choose based on intent:
|
|
189
|
+
|
|
190
|
+
```html
|
|
191
|
+
<!-- Decorative icon — hide from screen readers -->
|
|
192
|
+
<svg aria-hidden="true">...</svg>
|
|
193
|
+
|
|
194
|
+
<!-- Supplemental label — hide visually but announce -->
|
|
195
|
+
<span class="sr-only">Opens in a new tab</span>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### `!important` is intentional
|
|
199
|
+
|
|
200
|
+
All display utilities use `!important`. This is by design: utilities are
|
|
201
|
+
override-layer rules meant to win over component styles. If you find yourself
|
|
202
|
+
fighting a utility with `!important` in component CSS, that's a signal to
|
|
203
|
+
reconsider the component's default state rather than escalating specificity.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Display & Visibility Utilities
|
|
6
|
+
*
|
|
7
|
+
* CSS utility classes for showing/hiding elements at different breakpoints.
|
|
8
|
+
* All classes use `!important` to override component specificity.
|
|
9
|
+
*
|
|
10
|
+
* Use the viewport toolbar to verify responsive variants.
|
|
11
|
+
*/
|
|
12
|
+
const meta: Meta = {
|
|
13
|
+
title: "FP.React Components/Utilities/Display",
|
|
14
|
+
tags: ["stable"],
|
|
15
|
+
parameters: {
|
|
16
|
+
docs: {
|
|
17
|
+
description: {
|
|
18
|
+
component:
|
|
19
|
+
"Responsive display and visibility utilities. Switch viewports in the toolbar to verify breakpoint behavior.",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default meta;
|
|
26
|
+
type Story = StoryObj;
|
|
27
|
+
|
|
28
|
+
const boxStyle: React.CSSProperties = {
|
|
29
|
+
padding: "0.75rem 1rem",
|
|
30
|
+
background: "#e0f2fe",
|
|
31
|
+
border: "1px solid #0ea5e9",
|
|
32
|
+
borderRadius: "0.25rem",
|
|
33
|
+
fontFamily: "monospace",
|
|
34
|
+
fontSize: "0.875rem",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const hiddenBoxStyle: React.CSSProperties = {
|
|
38
|
+
...boxStyle,
|
|
39
|
+
background: "#fef9c3",
|
|
40
|
+
border: "1px solid #ca8a04",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** `.hide` removes from layout + accessibility tree. `.show` restores it. */
|
|
44
|
+
export const HideShow: Story = {
|
|
45
|
+
render: () => (
|
|
46
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "0.5rem" }}>
|
|
47
|
+
<div style={boxStyle}>Visible (no class)</div>
|
|
48
|
+
<div className="hide" style={hiddenBoxStyle}>
|
|
49
|
+
Hidden — .hide (display: none)
|
|
50
|
+
</div>
|
|
51
|
+
<div className="show" style={boxStyle}>
|
|
52
|
+
Always shown — .show (display: revert)
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** `.invisible` hides visually but preserves layout space (ghost element). */
|
|
59
|
+
export const Invisible: Story = {
|
|
60
|
+
render: () => (
|
|
61
|
+
<div style={{ display: "flex", gap: "0.5rem" }}>
|
|
62
|
+
<div style={boxStyle}>Before</div>
|
|
63
|
+
<div className="invisible" style={hiddenBoxStyle}>
|
|
64
|
+
.invisible — layout preserved, visually hidden
|
|
65
|
+
</div>
|
|
66
|
+
<div style={boxStyle}>After (notice the gap)</div>
|
|
67
|
+
</div>
|
|
68
|
+
),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* `.sr-only` — visually hidden but present in the accessibility tree.
|
|
73
|
+
* `.sr-only-focusable` — sr-only that becomes visible on focus (skip links).
|
|
74
|
+
*/
|
|
75
|
+
export const ScreenReaderOnly: Story = {
|
|
76
|
+
render: () => (
|
|
77
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "0.75rem" }}>
|
|
78
|
+
<p style={{ margin: 0 }}>
|
|
79
|
+
The text below is visually hidden but read by screen readers:
|
|
80
|
+
</p>
|
|
81
|
+
<span className="sr-only">
|
|
82
|
+
This text is only announced by screen readers
|
|
83
|
+
</span>
|
|
84
|
+
<a href="#main" className="sr-only-focusable" style={boxStyle}>
|
|
85
|
+
Tab to focus me — .sr-only-focusable (skip link pattern)
|
|
86
|
+
</a>
|
|
87
|
+
</div>
|
|
88
|
+
),
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/** `.print:hide` — hidden when printing. Open print preview to verify. */
|
|
92
|
+
export const PrintHide: Story = {
|
|
93
|
+
render: () => (
|
|
94
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "0.5rem" }}>
|
|
95
|
+
<div style={boxStyle}>Always visible (printed)</div>
|
|
96
|
+
<nav className="print:hide" style={hiddenBoxStyle}>
|
|
97
|
+
.print:hide — visible on screen, hidden in print
|
|
98
|
+
</nav>
|
|
99
|
+
</div>
|
|
100
|
+
),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Responsive hide/show.
|
|
105
|
+
* Switch the viewport toolbar to verify each breakpoint:
|
|
106
|
+
* - sm (480px): `.sm:hide` disappears
|
|
107
|
+
* - md (768px): `.md:show` appears
|
|
108
|
+
* - lg (992px): `.lg:hide` disappears
|
|
109
|
+
*/
|
|
110
|
+
export const ResponsiveVariants: Story = {
|
|
111
|
+
render: () => (
|
|
112
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "0.5rem" }}>
|
|
113
|
+
<div style={boxStyle}>Always visible</div>
|
|
114
|
+
<div className="sm:hide" style={hiddenBoxStyle}>
|
|
115
|
+
.sm:hide — hidden at 480px+
|
|
116
|
+
</div>
|
|
117
|
+
<div className="hide md:show" style={boxStyle}>
|
|
118
|
+
.hide .md:show — hidden by default, shown at 768px+
|
|
119
|
+
</div>
|
|
120
|
+
<div className="lg:hide" style={hiddenBoxStyle}>
|
|
121
|
+
.lg:hide — hidden at 992px+
|
|
122
|
+
</div>
|
|
123
|
+
<div className="hide xl:show" style={boxStyle}>
|
|
124
|
+
.hide .xl:show — hidden by default, shown at 1280px+
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/** `.md:invisible` at md+ — layout space preserved, element invisible. */
|
|
131
|
+
export const ResponsiveInvisible: Story = {
|
|
132
|
+
render: () => (
|
|
133
|
+
<div style={{ display: "flex", gap: "0.5rem" }}>
|
|
134
|
+
<div style={boxStyle}>Before</div>
|
|
135
|
+
<div className="md:invisible" style={hiddenBoxStyle}>
|
|
136
|
+
.md:invisible — invisible at 768px+ (layout preserved)
|
|
137
|
+
</div>
|
|
138
|
+
<div style={boxStyle}>After</div>
|
|
139
|
+
</div>
|
|
140
|
+
),
|
|
141
|
+
};
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/* Screen reader only utility class */
|
|
2
|
-
.sr-only {
|
|
3
|
-
position: absolute;
|
|
4
|
-
width: 1px;
|
|
5
|
-
height: 1px;
|
|
6
|
-
padding: 0;
|
|
7
|
-
margin: -1px;
|
|
8
|
-
overflow: hidden;
|
|
9
|
-
clip: rect(0, 0, 0, 0);
|
|
10
|
-
white-space: nowrap;
|
|
11
|
-
border-width: 0;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
1
|
[role=alert] {
|
|
15
2
|
/* Success colors - WCAG AA compliant (mapped to semantic tokens) */
|
|
16
3
|
--alert-success-bg: var(--color-success-bg);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sourceRoot":"","sources":["../../components/alert/alert.scss"],"names":[],"mappings":"AAAA;
|
|
1
|
+
{"version":3,"sourceRoot":"","sources":["../../components/alert/alert.scss"],"names":[],"mappings":"AAAA;AACE;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;AAEA;EACA;EAEA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA,YACE;EAEF;EACA;AAWA;AAMA;AAmEA;AA8BA;AAKA;;AArHA;EACE;EACA;;AAGF;EAlDF;IAmDI;;;AAIF;EACE;EACA;;AAIF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;AAEA;AASA;;AARA;EACE;EACA;EACA;EACA;EACA;;AAIF;AAAA;AAAA;AAAA;AAAA;EAKE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAIA;EACE;EACA;;AAKJ;EACE;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAKJ;EACE;;AAIF;AACE;;AAGF;EACE","file":"alert.css"}
|
|
@@ -1,31 +1,70 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--icon-btn-size: 3rem;
|
|
3
|
+
--icon-btn-gap: 0.375rem;
|
|
4
|
+
--icon-btn-padding-inline: 0.75rem;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
[data-icon-btn] [data-icon-label],
|
|
8
|
+
[data-icon-btn] .icon-label {
|
|
9
|
+
position: absolute;
|
|
10
|
+
width: 1px;
|
|
11
|
+
height: 1px;
|
|
12
|
+
padding: 0;
|
|
13
|
+
margin: -1px;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
clip: rect(0, 0, 0, 0);
|
|
16
|
+
clip-path: inset(50%);
|
|
17
|
+
white-space: nowrap;
|
|
18
|
+
border: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
1
21
|
button[data-icon-btn],
|
|
22
|
+
button.icon-btn,
|
|
23
|
+
[data-icon-btn],
|
|
2
24
|
.icon-btn {
|
|
3
25
|
--btn-color: currentColor;
|
|
26
|
+
padding: 0;
|
|
27
|
+
width: var(--icon-btn-size);
|
|
28
|
+
height: var(--icon-btn-size);
|
|
29
|
+
display: inline-grid;
|
|
30
|
+
place-items: center;
|
|
4
31
|
}
|
|
5
|
-
|
|
6
|
-
button[data-icon-btn~=has-label],
|
|
32
|
+
button[data-icon-btn][data-icon-btn~=has-label],
|
|
33
|
+
button.icon-btn[data-icon-btn~=has-label],
|
|
34
|
+
[data-icon-btn][data-icon-btn~=has-label],
|
|
7
35
|
.icon-btn[data-icon-btn~=has-label] {
|
|
8
|
-
|
|
9
|
-
|
|
36
|
+
width: max-content;
|
|
37
|
+
min-width: var(--icon-btn-size);
|
|
38
|
+
gap: var(--icon-btn-gap);
|
|
39
|
+
padding-inline: var(--icon-btn-padding-inline);
|
|
40
|
+
grid-auto-flow: column;
|
|
10
41
|
}
|
|
11
|
-
button[data-icon-btn~=has-label] [data-icon-label],
|
|
12
|
-
|
|
42
|
+
button[data-icon-btn][data-icon-btn~=has-label] [data-icon-label],
|
|
43
|
+
button[data-icon-btn][data-icon-btn~=has-label] .icon-label,
|
|
44
|
+
button.icon-btn[data-icon-btn~=has-label] [data-icon-label],
|
|
45
|
+
button.icon-btn[data-icon-btn~=has-label] .icon-label,
|
|
46
|
+
[data-icon-btn][data-icon-btn~=has-label] [data-icon-label],
|
|
47
|
+
[data-icon-btn][data-icon-btn~=has-label] .icon-label,
|
|
48
|
+
.icon-btn[data-icon-btn~=has-label] [data-icon-label],
|
|
49
|
+
.icon-btn[data-icon-btn~=has-label] .icon-label {
|
|
13
50
|
font-size: var(--btn-fs, 0.875rem);
|
|
14
51
|
line-height: 1;
|
|
15
52
|
white-space: nowrap;
|
|
16
53
|
}
|
|
17
54
|
|
|
18
|
-
@media (
|
|
19
|
-
[data-icon-label]
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
55
|
+
@media (min-width: 48rem) {
|
|
56
|
+
[data-icon-btn] [data-icon-label],
|
|
57
|
+
[data-icon-btn] .icon-label {
|
|
58
|
+
position: static;
|
|
59
|
+
width: auto;
|
|
60
|
+
height: auto;
|
|
61
|
+
padding: unset;
|
|
62
|
+
margin: unset;
|
|
63
|
+
overflow: visible;
|
|
64
|
+
clip: unset;
|
|
65
|
+
clip-path: unset;
|
|
27
66
|
white-space: nowrap;
|
|
28
|
-
border:
|
|
67
|
+
border: unset;
|
|
29
68
|
}
|
|
30
69
|
}
|
|
31
70
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sourceRoot":"","sources":["../../components/buttons/icon-button.scss"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sourceRoot":"","sources":["../../components/buttons/icon-button.scss"],"names":[],"mappings":"AAQA;EACE;EACA;EACA;;;AAKF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAOF;AAAA;AAAA;AAAA;EAIE;EAEA;EACA;EACA;EACA;EACA;;AAKA;AAAA;AAAA;AAAA;EACE;EACA;EACA;EACA;EACA;;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAEE;EACA;EACA;;;AAQN;EACE;AAAA;IAEE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA","file":"icon-button.css"}
|
|
@@ -76,6 +76,7 @@ dialog section {
|
|
|
76
76
|
.dialog-header button[type=button] {
|
|
77
77
|
background-color: var(--dialog-button-bg);
|
|
78
78
|
border: var(--dialog-button-border);
|
|
79
|
+
color: var(--dialog-close-color);
|
|
79
80
|
cursor: pointer;
|
|
80
81
|
/* Keyboard focus indicator */
|
|
81
82
|
/* Remove default focus for mouse users */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sourceRoot":"","sources":["../../components/dialog/dialog.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;IACE;IACA;IACA;IACA;IACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;;AACA;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;AAOA;AAQA;;AAbA;EACE;EACA;;AAIF;EACE;EACA;EACA;EACA;;AAIF;EACE;;;AAKN;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;AAEA;;AACA;AAAA;EACE;EACA;;AAGF;AAAA;EACE","file":"dialog.css"}
|
|
1
|
+
{"version":3,"sourceRoot":"","sources":["../../components/dialog/dialog.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;IACE;IACA;IACA;IACA;IACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;;AACA;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;AAOA;AAQA;;AAbA;EACE;EACA;;AAIF;EACE;EACA;EACA;EACA;;AAIF;EACE;;;AAKN;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;AAEA;;AACA;AAAA;EACE;EACA;;AAGF;AAAA;EACE","file":"dialog.css"}
|