@marianmeres/stuic 3.192.0 → 3.194.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/AGENTS.md +2 -2
- package/API.md +25 -0
- package/README.md +1 -1
- package/dist/components/EmptyState/index.css +1 -1
- package/dist/components/Input/FieldLikeButton.svelte +3 -1
- package/dist/components/Input/index.css +3 -0
- package/dist/components/PhoneFrame/PhoneFrame.svelte +165 -0
- package/dist/components/PhoneFrame/PhoneFrame.svelte.d.ts +71 -0
- package/dist/components/PhoneFrame/README.md +260 -0
- package/dist/components/PhoneFrame/index.css +114 -0
- package/dist/components/PhoneFrame/index.d.ts +1 -0
- package/dist/components/PhoneFrame/index.js +1 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/docs/domains/components.md +2 -1
- package/package.json +23 -23
package/AGENTS.md
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
```
|
|
25
25
|
src/lib/
|
|
26
|
-
├── components/ #
|
|
26
|
+
├── components/ # 84 component directories
|
|
27
27
|
├── actions/ # 16 Svelte actions (use: directives)
|
|
28
28
|
├── attachments/ # Svelte attachments ({@attach} — preferred for new DOM helpers)
|
|
29
29
|
├── utils/ # 55 utility modules (48 on the barrel)
|
|
@@ -146,7 +146,7 @@ so it is the only confusable pair — do not "fix" one into the other.
|
|
|
146
146
|
|
|
147
147
|
### Domain Docs
|
|
148
148
|
|
|
149
|
-
- [Components](./docs/domains/components.md) —
|
|
149
|
+
- [Components](./docs/domains/components.md) — 84 component directories, Props pattern, snippets
|
|
150
150
|
- [Theming](./docs/domains/theming.md) — CSS tokens, dark mode, themes
|
|
151
151
|
- [CSS presets](./docs/domains/css-presets.md) — ratio-locked frame (letterbox), safe-area, scrollbar
|
|
152
152
|
- [Actions](./docs/domains/actions.md) — 16 Svelte directives
|
package/API.md
CHANGED
|
@@ -1363,6 +1363,30 @@ interface ImageCyclerImage {
|
|
|
1363
1363
|
}
|
|
1364
1364
|
```
|
|
1365
1365
|
|
|
1366
|
+
#### `PhoneFrame`
|
|
1367
|
+
|
|
1368
|
+
Phone-shaped bezel around a screenshot (`src`) or any content (`children`). Block-level, sized with `class`; the screen is locked to a `390 / 844` ratio unless `screenHeight` turns it into a fixed-height window showing the top of the content. With `href` the whole frame is a link.
|
|
1369
|
+
|
|
1370
|
+
| Prop | Type | Default | Description |
|
|
1371
|
+
| --------------- | ------------------------------- | -------- | --------------------------------------------------------------------------- |
|
|
1372
|
+
| `src` | `string` | — | Screenshot URL (`object-fit: cover`, anchored to the top) |
|
|
1373
|
+
| `alt` | `string` | `""` | Alt text; the link's accessible name with `href` |
|
|
1374
|
+
| `width` | `number \| string` | — | Intrinsic pixel width of the screenshot (`<img width>`), not the frame size |
|
|
1375
|
+
| `height` | `number \| string` | — | Intrinsic pixel height of the screenshot (`<img height>`) |
|
|
1376
|
+
| `srcset` | `string` | — | `<img srcset>` pass-through |
|
|
1377
|
+
| `sizes` | `string` | — | `<img sizes>` pass-through |
|
|
1378
|
+
| `loading` | `"lazy" \| "eager"` | `"lazy"` | `eager` for a hero screenshot |
|
|
1379
|
+
| `fetchpriority` | `"high" \| "low" \| "auto"` | — | `<img fetchpriority>` pass-through |
|
|
1380
|
+
| `children` | `Snippet` | — | Custom screen content; takes precedence over `src` |
|
|
1381
|
+
| `href` | `string` | — | Renders `<a>` instead of `<div>` |
|
|
1382
|
+
| `target` | `string` | — | Link target |
|
|
1383
|
+
| `rel` | `string` | — | Link rel; `"noopener"` by default when `target` is `"_blank"` |
|
|
1384
|
+
| `aspectRatio` | `string \| number` | token | Screen `aspect-ratio` (`"390 / 844"`); ignored while `screenHeight` is set |
|
|
1385
|
+
| `screenHeight` | `string` | — | Fixed screen height: a clipped window of the top of the content |
|
|
1386
|
+
| `notch` | `"none" \| "island" \| "notch"` | `"none"` | Camera cutout over the top of the screen |
|
|
1387
|
+
| `classScreen` | `string` | — | Classes for the screen (clipping window) |
|
|
1388
|
+
| `classImage` | `string` | — | Classes for the `<img>` |
|
|
1389
|
+
|
|
1366
1390
|
#### `ThemePreview`
|
|
1367
1391
|
|
|
1368
1392
|
Theme color swatch preview.
|
|
@@ -2503,6 +2527,7 @@ Each component defines customization tokens. Override globally in `:root {}` or
|
|
|
2503
2527
|
| Notifications | `--stuic-notification-*` | `bg`, `text`, `border` |
|
|
2504
2528
|
| Tooltip | `--stuic-tooltip-*` | `bg`, `text` |
|
|
2505
2529
|
| Popover | `--stuic-popover-*` | `bg`, `text`, `border` |
|
|
2530
|
+
| PhoneFrame | `--stuic-phone-frame-*` | `bg`, `padding`, `radius`, `border-color`, `shadow`, `screen-bg`, `screen-radius`, `screen-aspect-ratio`, `screen-height`, `island-width`, `island-top`, `notch-width`, `notch-radius`, `notch-bg`, `ring-color` |
|
|
2506
2531
|
| Skeleton | `--stuic-skeleton-*` | `bg`, `bg-highlight`, `duration` |
|
|
2507
2532
|
| Spotlight | `--stuic-spotlight-*` | `backdrop-bg`, `annotation-bg`, `annotation-text`, `annotation-border` |
|
|
2508
2533
|
| Cart | `--stuic-cart-*` | `gap`, `item-padding`, `item-radius`, `item-border-color`, `item-bg`, `thumbnail-size`, `quantity-border-color`, `remove-color`, `summary-border-color`, `compact-max-height`, `transition` |
|
package/README.md
CHANGED
|
@@ -191,7 +191,7 @@ CommandMenu, DropdownMenu, TabbedMenu, TypeaheadInput, KbdShortcut
|
|
|
191
191
|
|
|
192
192
|
### Display & Utility
|
|
193
193
|
|
|
194
|
-
Avatar, Pill, Book, BookResponsive, Card, Carousel, Circle, AnimatedElipsis, H, IconSwap, ImageCycler, Separator, ThemePreview, Tree, ColorScheme, Thc, HoverExpandableWidth, AssetsPreview, AssetsPreviewInline, DataTable
|
|
194
|
+
Avatar, Pill, Book, BookResponsive, Card, Carousel, Circle, AnimatedElipsis, H, IconSwap, ImageCycler, PhoneFrame, Separator, ThemePreview, Tree, ColorScheme, Thc, HoverExpandableWidth, AssetsPreview, AssetsPreviewInline, DataTable
|
|
195
195
|
|
|
196
196
|
### E-commerce
|
|
197
197
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
--stuic-empty-state-padding-sm: 1.5rem;
|
|
13
13
|
--stuic-empty-state-icon-size-sm: 2rem;
|
|
14
14
|
--stuic-empty-state-title-font-size-sm: var(--text-base);
|
|
15
|
-
--stuic-empty-state-description-font-size-sm: var(--text-
|
|
15
|
+
--stuic-empty-state-description-font-size-sm: var(--text-sm);
|
|
16
16
|
|
|
17
17
|
/* Size: md */
|
|
18
18
|
--stuic-empty-state-padding-md: 3rem;
|
|
@@ -201,7 +201,9 @@
|
|
|
201
201
|
{...rest as any}
|
|
202
202
|
{tabindex}
|
|
203
203
|
>
|
|
204
|
-
|
|
204
|
+
<!-- leading-normal: Button's `line-height: 1` makes this box 1em tall, and `truncate`'s
|
|
205
|
+
overflow:hidden then cuts descenders (g, y) and stacked accents (Č, Å) -->
|
|
206
|
+
<span class="block truncate leading-normal">
|
|
205
207
|
{#if typeof rendered === "function"}
|
|
206
208
|
{@render rendered(value)}
|
|
207
209
|
{:else if rendered}
|
|
@@ -669,6 +669,9 @@
|
|
|
669
669
|
|
|
670
670
|
.stuic-field-options-chip-label {
|
|
671
671
|
min-width: 0;
|
|
672
|
+
/* the Pill's `line-height: 1` box is 1em tall, so the overflow clip below would cut
|
|
673
|
+
descenders (g, y) and stacked accents (Č, Å) */
|
|
674
|
+
line-height: 1.5;
|
|
672
675
|
overflow: hidden;
|
|
673
676
|
text-overflow: ellipsis;
|
|
674
677
|
white-space: nowrap;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
|
|
5
|
+
/** Camera cutout drawn over the top of the screen. */
|
|
6
|
+
export type PhoneFrameNotch = "none" | "island" | "notch";
|
|
7
|
+
|
|
8
|
+
export interface Props extends Omit<HTMLAttributes<HTMLElement>, "children" | "class"> {
|
|
9
|
+
/**
|
|
10
|
+
* Screenshot URL. The image fills the screen (`object-fit: cover`, anchored to the
|
|
11
|
+
* top). Omit it and render your own content with `children` instead.
|
|
12
|
+
*/
|
|
13
|
+
src?: string;
|
|
14
|
+
/** Alt text of the screenshot. With `href` this is also the link's accessible name. */
|
|
15
|
+
alt?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Intrinsic pixel width of the screenshot (the `<img width>` attribute, so the
|
|
18
|
+
* browser reserves space before the image loads). This does NOT size the frame —
|
|
19
|
+
* size the frame with `class` (e.g. `w-64`).
|
|
20
|
+
*/
|
|
21
|
+
width?: number | string;
|
|
22
|
+
/** Intrinsic pixel height of the screenshot (see `width`). */
|
|
23
|
+
height?: number | string;
|
|
24
|
+
/** `<img srcset>` pass-through, for 1×/2× screenshots. */
|
|
25
|
+
srcset?: string;
|
|
26
|
+
/** `<img sizes>` pass-through. */
|
|
27
|
+
sizes?: string;
|
|
28
|
+
/** Image loading. `lazy` by default; use `eager` for a hero (above-the-fold) screenshot. */
|
|
29
|
+
loading?: "lazy" | "eager";
|
|
30
|
+
/** `<img fetchpriority>` pass-through. `high` for the LCP screenshot. */
|
|
31
|
+
fetchpriority?: "high" | "low" | "auto";
|
|
32
|
+
/**
|
|
33
|
+
* Custom screen content (an iframe, a live component, a video...). Takes precedence
|
|
34
|
+
* over `src`. The screen clips it and fixes its size; make the content fill it
|
|
35
|
+
* (`w-full h-full`).
|
|
36
|
+
*/
|
|
37
|
+
children?: Snippet;
|
|
38
|
+
/** Makes the whole frame a link: renders `<a>` instead of `<div>`. */
|
|
39
|
+
href?: string;
|
|
40
|
+
/** Link target (e.g. `"_blank"`). Only relevant when `href` is set. */
|
|
41
|
+
target?: string;
|
|
42
|
+
/** Link `rel`. Defaults to `"noopener"` when `target` is `"_blank"`. */
|
|
43
|
+
rel?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Screen aspect ratio as a CSS `aspect-ratio` value (`"390 / 844"`, `390 / 844`,
|
|
46
|
+
* `0.46`). Defaults to the `--stuic-phone-frame-screen-aspect-ratio` token
|
|
47
|
+
* (`390 / 844`, an iPhone 14 class screen in CSS px). Ignored while `screenHeight`
|
|
48
|
+
* is set.
|
|
49
|
+
*/
|
|
50
|
+
aspectRatio?: string | number;
|
|
51
|
+
/**
|
|
52
|
+
* Fixed screen height (any CSS length). The screen becomes a window of that height
|
|
53
|
+
* showing the TOP of the content, the rest is clipped and the aspect ratio no
|
|
54
|
+
* longer applies. For a responsive version (clipped on narrow screens, whole phone
|
|
55
|
+
* from a breakpoint up) set the `--stuic-phone-frame-screen-height` token through
|
|
56
|
+
* `class` instead — see the README.
|
|
57
|
+
*/
|
|
58
|
+
screenHeight?: string;
|
|
59
|
+
/** Camera cutout drawn over the top of the screen. `none` by default. */
|
|
60
|
+
notch?: PhoneFrameNotch;
|
|
61
|
+
/** Skip all default styling */
|
|
62
|
+
unstyled?: boolean;
|
|
63
|
+
/** Additional CSS classes for the frame (the bezel). Size the frame here. */
|
|
64
|
+
class?: string;
|
|
65
|
+
/** Additional CSS classes for the screen (the clipping window inside the bezel). */
|
|
66
|
+
classScreen?: string;
|
|
67
|
+
/** Additional CSS classes for the `<img>`. */
|
|
68
|
+
classImage?: string;
|
|
69
|
+
/** Bindable element reference (the `<div>` or `<a>`). */
|
|
70
|
+
el?: HTMLElement;
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<script lang="ts">
|
|
75
|
+
import { twMerge } from "../../utils/tw-merge.js";
|
|
76
|
+
|
|
77
|
+
let {
|
|
78
|
+
src,
|
|
79
|
+
alt = "",
|
|
80
|
+
width,
|
|
81
|
+
height,
|
|
82
|
+
srcset,
|
|
83
|
+
sizes,
|
|
84
|
+
loading = "lazy",
|
|
85
|
+
fetchpriority,
|
|
86
|
+
children,
|
|
87
|
+
href,
|
|
88
|
+
target,
|
|
89
|
+
rel,
|
|
90
|
+
aspectRatio,
|
|
91
|
+
screenHeight,
|
|
92
|
+
notch = "none",
|
|
93
|
+
unstyled = false,
|
|
94
|
+
class: classProp,
|
|
95
|
+
classScreen: classScreenProp,
|
|
96
|
+
classImage: classImageProp,
|
|
97
|
+
el = $bindable(),
|
|
98
|
+
...rest
|
|
99
|
+
}: Props = $props();
|
|
100
|
+
|
|
101
|
+
let _class = $derived(unstyled ? classProp : twMerge("stuic-phone-frame", classProp));
|
|
102
|
+
let _classScreen = $derived(
|
|
103
|
+
unstyled ? classScreenProp : twMerge("stuic-phone-frame-screen", classScreenProp)
|
|
104
|
+
);
|
|
105
|
+
let _classImage = $derived(
|
|
106
|
+
unstyled ? classImageProp : twMerge("stuic-phone-frame-image", classImageProp)
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Prop-driven tokens go on the screen itself (closest to where they are read), so
|
|
110
|
+
// `style` from rest props stays untouched on the root.
|
|
111
|
+
let _screenStyle = $derived.by(() => {
|
|
112
|
+
const styles: string[] = [];
|
|
113
|
+
if (aspectRatio !== undefined && aspectRatio !== "") {
|
|
114
|
+
styles.push(`--stuic-phone-frame-screen-aspect-ratio: ${aspectRatio}`);
|
|
115
|
+
}
|
|
116
|
+
if (screenHeight) styles.push(`--stuic-phone-frame-screen-height: ${screenHeight}`);
|
|
117
|
+
return styles.length ? styles.join("; ") : undefined;
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
let _notch = $derived(!unstyled && notch !== "none" ? notch : undefined);
|
|
121
|
+
|
|
122
|
+
let _rel = $derived(rel ?? (target === "_blank" ? "noopener" : undefined));
|
|
123
|
+
</script>
|
|
124
|
+
|
|
125
|
+
{#snippet screen()}
|
|
126
|
+
<div class={_classScreen} style={_screenStyle}>
|
|
127
|
+
{#if children}
|
|
128
|
+
{@render children()}
|
|
129
|
+
{:else if src}
|
|
130
|
+
<img
|
|
131
|
+
{src}
|
|
132
|
+
{alt}
|
|
133
|
+
{width}
|
|
134
|
+
{height}
|
|
135
|
+
{srcset}
|
|
136
|
+
{sizes}
|
|
137
|
+
{loading}
|
|
138
|
+
{fetchpriority}
|
|
139
|
+
decoding="async"
|
|
140
|
+
class={_classImage}
|
|
141
|
+
/>
|
|
142
|
+
{/if}
|
|
143
|
+
{#if _notch}
|
|
144
|
+
<div class="stuic-phone-frame-notch" aria-hidden="true"></div>
|
|
145
|
+
{/if}
|
|
146
|
+
</div>
|
|
147
|
+
{/snippet}
|
|
148
|
+
|
|
149
|
+
{#if href}
|
|
150
|
+
<a
|
|
151
|
+
bind:this={el}
|
|
152
|
+
{href}
|
|
153
|
+
{target}
|
|
154
|
+
rel={_rel}
|
|
155
|
+
class={_class}
|
|
156
|
+
data-notch={_notch}
|
|
157
|
+
{...rest}
|
|
158
|
+
>
|
|
159
|
+
{@render screen()}
|
|
160
|
+
</a>
|
|
161
|
+
{:else}
|
|
162
|
+
<div bind:this={el} class={_class} data-notch={_notch} {...rest}>
|
|
163
|
+
{@render screen()}
|
|
164
|
+
</div>
|
|
165
|
+
{/if}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
/** Camera cutout drawn over the top of the screen. */
|
|
4
|
+
export type PhoneFrameNotch = "none" | "island" | "notch";
|
|
5
|
+
export interface Props extends Omit<HTMLAttributes<HTMLElement>, "children" | "class"> {
|
|
6
|
+
/**
|
|
7
|
+
* Screenshot URL. The image fills the screen (`object-fit: cover`, anchored to the
|
|
8
|
+
* top). Omit it and render your own content with `children` instead.
|
|
9
|
+
*/
|
|
10
|
+
src?: string;
|
|
11
|
+
/** Alt text of the screenshot. With `href` this is also the link's accessible name. */
|
|
12
|
+
alt?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Intrinsic pixel width of the screenshot (the `<img width>` attribute, so the
|
|
15
|
+
* browser reserves space before the image loads). This does NOT size the frame —
|
|
16
|
+
* size the frame with `class` (e.g. `w-64`).
|
|
17
|
+
*/
|
|
18
|
+
width?: number | string;
|
|
19
|
+
/** Intrinsic pixel height of the screenshot (see `width`). */
|
|
20
|
+
height?: number | string;
|
|
21
|
+
/** `<img srcset>` pass-through, for 1×/2× screenshots. */
|
|
22
|
+
srcset?: string;
|
|
23
|
+
/** `<img sizes>` pass-through. */
|
|
24
|
+
sizes?: string;
|
|
25
|
+
/** Image loading. `lazy` by default; use `eager` for a hero (above-the-fold) screenshot. */
|
|
26
|
+
loading?: "lazy" | "eager";
|
|
27
|
+
/** `<img fetchpriority>` pass-through. `high` for the LCP screenshot. */
|
|
28
|
+
fetchpriority?: "high" | "low" | "auto";
|
|
29
|
+
/**
|
|
30
|
+
* Custom screen content (an iframe, a live component, a video...). Takes precedence
|
|
31
|
+
* over `src`. The screen clips it and fixes its size; make the content fill it
|
|
32
|
+
* (`w-full h-full`).
|
|
33
|
+
*/
|
|
34
|
+
children?: Snippet;
|
|
35
|
+
/** Makes the whole frame a link: renders `<a>` instead of `<div>`. */
|
|
36
|
+
href?: string;
|
|
37
|
+
/** Link target (e.g. `"_blank"`). Only relevant when `href` is set. */
|
|
38
|
+
target?: string;
|
|
39
|
+
/** Link `rel`. Defaults to `"noopener"` when `target` is `"_blank"`. */
|
|
40
|
+
rel?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Screen aspect ratio as a CSS `aspect-ratio` value (`"390 / 844"`, `390 / 844`,
|
|
43
|
+
* `0.46`). Defaults to the `--stuic-phone-frame-screen-aspect-ratio` token
|
|
44
|
+
* (`390 / 844`, an iPhone 14 class screen in CSS px). Ignored while `screenHeight`
|
|
45
|
+
* is set.
|
|
46
|
+
*/
|
|
47
|
+
aspectRatio?: string | number;
|
|
48
|
+
/**
|
|
49
|
+
* Fixed screen height (any CSS length). The screen becomes a window of that height
|
|
50
|
+
* showing the TOP of the content, the rest is clipped and the aspect ratio no
|
|
51
|
+
* longer applies. For a responsive version (clipped on narrow screens, whole phone
|
|
52
|
+
* from a breakpoint up) set the `--stuic-phone-frame-screen-height` token through
|
|
53
|
+
* `class` instead — see the README.
|
|
54
|
+
*/
|
|
55
|
+
screenHeight?: string;
|
|
56
|
+
/** Camera cutout drawn over the top of the screen. `none` by default. */
|
|
57
|
+
notch?: PhoneFrameNotch;
|
|
58
|
+
/** Skip all default styling */
|
|
59
|
+
unstyled?: boolean;
|
|
60
|
+
/** Additional CSS classes for the frame (the bezel). Size the frame here. */
|
|
61
|
+
class?: string;
|
|
62
|
+
/** Additional CSS classes for the screen (the clipping window inside the bezel). */
|
|
63
|
+
classScreen?: string;
|
|
64
|
+
/** Additional CSS classes for the `<img>`. */
|
|
65
|
+
classImage?: string;
|
|
66
|
+
/** Bindable element reference (the `<div>` or `<a>`). */
|
|
67
|
+
el?: HTMLElement;
|
|
68
|
+
}
|
|
69
|
+
declare const PhoneFrame: import("svelte").Component<Props, {}, "el">;
|
|
70
|
+
type PhoneFrame = ReturnType<typeof PhoneFrame>;
|
|
71
|
+
export default PhoneFrame;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# PhoneFrame
|
|
2
|
+
|
|
3
|
+
A phone-shaped bezel around a screenshot — the "picture of the product" on a landing page, a
|
|
4
|
+
preview of a page you are about to publish, a device mockup in docs. The screen holds an `<img>`
|
|
5
|
+
(`src`), or any content you render yourself (`children`: an iframe, a live component, a video).
|
|
6
|
+
With `href` the whole frame is a link to the page it pictures.
|
|
7
|
+
|
|
8
|
+
The frame is a block element and fills its container: **size it with `class`** (`w-64`,
|
|
9
|
+
`max-w-xs mx-auto`, a grid column). The screen is locked to a phone aspect ratio (`390 / 844`,
|
|
10
|
+
an iPhone 14 class screen in CSS px) and derives its height from the width, so the frame scales
|
|
11
|
+
with its column. Set `screenHeight` to turn the screen into a fixed-height window that shows only
|
|
12
|
+
the top of the content — what a visitor sees first — with the rest clipped.
|
|
13
|
+
|
|
14
|
+
## Props
|
|
15
|
+
|
|
16
|
+
| Prop | Type | Default | Description |
|
|
17
|
+
| --------------- | ------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
18
|
+
| `src` | `string` | - | Screenshot URL. Fills the screen (`object-fit: cover`, anchored to the top) |
|
|
19
|
+
| `alt` | `string` | `""` | Alt text of the screenshot. With `href` it is also the link's accessible name |
|
|
20
|
+
| `width` | `number \| string` | - | Intrinsic pixel width of the screenshot (`<img width>`, reserves space before load). Does **not** size the frame — use `class` |
|
|
21
|
+
| `height` | `number \| string` | - | Intrinsic pixel height of the screenshot (`<img height>`) |
|
|
22
|
+
| `srcset` | `string` | - | `<img srcset>` pass-through (1×/2× screenshots) |
|
|
23
|
+
| `sizes` | `string` | - | `<img sizes>` pass-through |
|
|
24
|
+
| `loading` | `"lazy" \| "eager"` | `"lazy"` | Image loading. `eager` for a hero (above-the-fold) screenshot |
|
|
25
|
+
| `fetchpriority` | `"high" \| "low" \| "auto"` | - | `<img fetchpriority>` pass-through. `high` for the LCP screenshot |
|
|
26
|
+
| `children` | `Snippet` | - | Custom screen content; takes precedence over `src`. Make it fill the screen (`w-full h-full`) |
|
|
27
|
+
| `href` | `string` | - | Makes the whole frame a link (`<a>` instead of `<div>`) |
|
|
28
|
+
| `target` | `string` | - | Link target (e.g. `"_blank"`). Only with `href` |
|
|
29
|
+
| `rel` | `string` | - | Link `rel`. Defaults to `"noopener"` when `target` is `"_blank"` |
|
|
30
|
+
| `aspectRatio` | `string \| number` | token | Screen aspect ratio, any CSS `aspect-ratio` value (`"390 / 844"`, `390 / 844`, `0.46`). Ignored while `screenHeight` is set |
|
|
31
|
+
| `screenHeight` | `string` | - | Fixed screen height (CSS length): a window showing the top of the content, the rest clipped, the ratio dropped. See Clipped window |
|
|
32
|
+
| `notch` | `"none" \| "island" \| "notch"` | `"none"` | Camera cutout drawn over the top of the screen |
|
|
33
|
+
| `unstyled` | `boolean` | `false` | Skip all default styling |
|
|
34
|
+
| `class` | `string` | - | Classes for the frame (the bezel). Size the frame here |
|
|
35
|
+
| `classScreen` | `string` | - | Classes for the screen (the clipping window inside the bezel) |
|
|
36
|
+
| `classImage` | `string` | - | Classes for the `<img>` |
|
|
37
|
+
| `el` | `HTMLElement` | - | Bindable element reference (the `<div>` or `<a>`) |
|
|
38
|
+
|
|
39
|
+
Other attributes (`style`, `title`, `aria-*`, `data-*`, `onclick`, …) are spread onto the frame
|
|
40
|
+
element.
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
### A screenshot
|
|
45
|
+
|
|
46
|
+
```svelte
|
|
47
|
+
<script lang="ts">
|
|
48
|
+
import { PhoneFrame } from "@marianmeres/stuic";
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<PhoneFrame
|
|
52
|
+
class="w-64"
|
|
53
|
+
src="/examples/apartment-page.webp"
|
|
54
|
+
alt="The apartment's page on a phone: Wi-Fi name and password, the key box code"
|
|
55
|
+
width={780}
|
|
56
|
+
height={1688}
|
|
57
|
+
/>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Take the screenshot at the screen's CSS size (390×844) on a 2× display and pass the file's pixel
|
|
61
|
+
size as `width`/`height`. With `object-fit: cover` a screenshot of a different ratio still fills the
|
|
62
|
+
screen; the top stays visible and the bottom is cropped.
|
|
63
|
+
|
|
64
|
+
### A link to the live page
|
|
65
|
+
|
|
66
|
+
```svelte
|
|
67
|
+
<PhoneFrame
|
|
68
|
+
class="w-64"
|
|
69
|
+
src="/examples/apartment-page.webp"
|
|
70
|
+
alt="Open the live apartment page"
|
|
71
|
+
width={780}
|
|
72
|
+
height={1688}
|
|
73
|
+
href="/i/abc123"
|
|
74
|
+
target="_blank"
|
|
75
|
+
/>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`target="_blank"` gets `rel="noopener"` unless you pass `rel` yourself. The `alt` is the link's
|
|
79
|
+
name — write it as the action, not as a picture description.
|
|
80
|
+
|
|
81
|
+
### The hero screenshot (LCP)
|
|
82
|
+
|
|
83
|
+
Everything below the fold stays lazy; the one in the hero should not:
|
|
84
|
+
|
|
85
|
+
```svelte
|
|
86
|
+
<PhoneFrame
|
|
87
|
+
class="w-72"
|
|
88
|
+
{src}
|
|
89
|
+
{alt}
|
|
90
|
+
{width}
|
|
91
|
+
{height}
|
|
92
|
+
loading="eager"
|
|
93
|
+
fetchpriority="high"
|
|
94
|
+
/>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Clipped window: the first screen only
|
|
98
|
+
|
|
99
|
+
On a narrow viewport a whole phone is a tall picture of mostly scrolled-away content. Show the
|
|
100
|
+
top rows at a fixed height instead:
|
|
101
|
+
|
|
102
|
+
```svelte
|
|
103
|
+
<PhoneFrame class="w-64" {src} {alt} {width} {height} screenHeight="20rem" />
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
For the common responsive version — clipped on narrow screens, the whole phone from a breakpoint
|
|
107
|
+
up — set the token through `class` (it is inherited by the screen) and leave `screenHeight` unset:
|
|
108
|
+
|
|
109
|
+
```svelte
|
|
110
|
+
<PhoneFrame
|
|
111
|
+
class="w-full lg:w-72 [--stuic-phone-frame-screen-height:20rem] lg:[--stuic-phone-frame-screen-height:auto]"
|
|
112
|
+
{src}
|
|
113
|
+
{alt}
|
|
114
|
+
{width}
|
|
115
|
+
{height}
|
|
116
|
+
/>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Don't combine the two: the `screenHeight` prop is an inline declaration on the screen and beats
|
|
120
|
+
an inherited token at every breakpoint.
|
|
121
|
+
|
|
122
|
+
### Phone cut off by the section edge
|
|
123
|
+
|
|
124
|
+
The other hero pattern — a phone emerging from the bottom of the section — needs no component
|
|
125
|
+
support. Clip the frame with your own wrapper:
|
|
126
|
+
|
|
127
|
+
```svelte
|
|
128
|
+
<div class="max-h-80 overflow-hidden">
|
|
129
|
+
<PhoneFrame class="w-72" {src} {alt} {width} {height} />
|
|
130
|
+
</div>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Camera cutout
|
|
134
|
+
|
|
135
|
+
```svelte
|
|
136
|
+
<PhoneFrame class="w-64" {src} {alt} notch="island" />
|
|
137
|
+
<PhoneFrame class="w-64" {src} {alt} notch="notch" />
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The cutout is painted over the screen content in the bezel color, so a screenshot taken in a plain
|
|
141
|
+
browser window (no status bar) gets its top few pixels covered. It is `pointer-events: none` and
|
|
142
|
+
`aria-hidden`.
|
|
143
|
+
|
|
144
|
+
### Your own screen content
|
|
145
|
+
|
|
146
|
+
```svelte
|
|
147
|
+
<PhoneFrame class="w-72">
|
|
148
|
+
<iframe src="/preview" title="Live preview" class="w-full h-full border-0"></iframe>
|
|
149
|
+
</PhoneFrame>
|
|
150
|
+
|
|
151
|
+
<PhoneFrame class="w-72" notch="island">
|
|
152
|
+
<div class="w-full h-full overflow-y-auto bg-white p-4 pt-10">
|
|
153
|
+
<!-- a live component tree -->
|
|
154
|
+
</div>
|
|
155
|
+
</PhoneFrame>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The screen clips (`overflow: hidden`) and has a definite height, so `h-full` on the content
|
|
159
|
+
works. Scrolling content needs its own `overflow-y-auto`.
|
|
160
|
+
|
|
161
|
+
### Another device ratio
|
|
162
|
+
|
|
163
|
+
```svelte
|
|
164
|
+
<!-- a tablet: 3:4 screen, bigger bezel radius -->
|
|
165
|
+
<PhoneFrame
|
|
166
|
+
class="w-96"
|
|
167
|
+
aspectRatio="3 / 4"
|
|
168
|
+
style="--stuic-phone-frame-radius: 1.25rem; --stuic-phone-frame-padding: 0.75rem;"
|
|
169
|
+
{src}
|
|
170
|
+
{alt}
|
|
171
|
+
/>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Unstyled
|
|
175
|
+
|
|
176
|
+
```svelte
|
|
177
|
+
<PhoneFrame
|
|
178
|
+
unstyled
|
|
179
|
+
class="rounded-3xl bg-zinc-800 p-3"
|
|
180
|
+
classScreen="aspect-[9/19.5] overflow-hidden rounded-2xl"
|
|
181
|
+
classImage="h-full w-full object-cover"
|
|
182
|
+
{src}
|
|
183
|
+
{alt}
|
|
184
|
+
/>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`unstyled` drops every `stuic-phone-frame*` class and the cutout; the three class props are then
|
|
188
|
+
the only styling.
|
|
189
|
+
|
|
190
|
+
## CSS Variables
|
|
191
|
+
|
|
192
|
+
### Component Tokens
|
|
193
|
+
|
|
194
|
+
Override globally in `:root` or locally via the `style` prop:
|
|
195
|
+
|
|
196
|
+
| Variable | Default | Description |
|
|
197
|
+
| ----------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
198
|
+
| `--stuic-phone-frame-bg` | `#171717` | Bezel color |
|
|
199
|
+
| `--stuic-phone-frame-padding` | `0.5rem` | Bezel thickness |
|
|
200
|
+
| `--stuic-phone-frame-radius` | `1.75rem` | Bezel (outer) corner radius |
|
|
201
|
+
| `--stuic-phone-frame-border-width` | `--stuic-border-width` | Hairline around the bezel (fallback: the shared border-width tier) |
|
|
202
|
+
| `--stuic-phone-frame-border-color` | `rgb(0 0 0 / 0.4)` | Hairline color (`rgb(255 255 255 / 0.15)` in dark mode) |
|
|
203
|
+
| `--stuic-phone-frame-shadow` | `--stuic-shadow-dialog` | Drop shadow (fallback: the shared dialog shadow tier) |
|
|
204
|
+
| `--stuic-phone-frame-screen-bg` | `--stuic-color-background` | Screen color, visible before the image loads or behind letterboxed content |
|
|
205
|
+
| `--stuic-phone-frame-screen-radius` | concentric | Screen corner radius. Default: bezel radius − padding − border width |
|
|
206
|
+
| `--stuic-phone-frame-screen-aspect-ratio` | `390 / 844` | Screen ratio while the height is `auto`. The `aspectRatio` prop sets it inline |
|
|
207
|
+
| `--stuic-phone-frame-screen-height` | `auto` | A length makes the screen a clipped window of that height. The `screenHeight` prop sets it inline |
|
|
208
|
+
| `--stuic-phone-frame-island-width` | `32%` | Dynamic-island width, relative to the screen width (the height follows, 126:37) |
|
|
209
|
+
| `--stuic-phone-frame-island-top` | `3%` | Dynamic-island offset from the top. A `%` is relative to the screen **width** (it is a margin) |
|
|
210
|
+
| `--stuic-phone-frame-notch-width` | `54%` | Classic notch width, relative to the screen width (the height follows, 209:30) |
|
|
211
|
+
| `--stuic-phone-frame-notch-radius` | `1rem` | Classic notch bottom corner radius |
|
|
212
|
+
| `--stuic-phone-frame-notch-bg` | `--stuic-phone-frame-bg` | Cutout color (both variants) |
|
|
213
|
+
| `--stuic-phone-frame-ring-width` | `3px` | Focus ring width (linked frames, `:focus-visible`) |
|
|
214
|
+
| `--stuic-phone-frame-ring-offset` | `2px` | Focus ring offset |
|
|
215
|
+
| `--stuic-phone-frame-ring-color` | `--stuic-color-ring` | Focus ring color |
|
|
216
|
+
|
|
217
|
+
The radius, border width and shadow intentionally do **not** follow `--stuic-radius-container`:
|
|
218
|
+
a device bezel is a hardware shape, not UI chrome. The hairline and shadow do follow the shared
|
|
219
|
+
tiers, so a flat theme (`--stuic-border-width: 0; --stuic-shadow-dialog: none`) flattens the phone
|
|
220
|
+
too.
|
|
221
|
+
|
|
222
|
+
### Customization Examples
|
|
223
|
+
|
|
224
|
+
```css
|
|
225
|
+
/* Global — a light "silver" phone */
|
|
226
|
+
:root {
|
|
227
|
+
--stuic-phone-frame-bg: #e5e5e5;
|
|
228
|
+
--stuic-phone-frame-border-color: rgb(0 0 0 / 0.15);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* Global — thicker bezel, keeps the screen corners concentric */
|
|
232
|
+
:root {
|
|
233
|
+
--stuic-phone-frame-padding: 0.75rem;
|
|
234
|
+
--stuic-phone-frame-radius: 2.25rem;
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
```svelte
|
|
239
|
+
<!-- Local — no shadow, flat corners -->
|
|
240
|
+
<PhoneFrame
|
|
241
|
+
style="--stuic-phone-frame-shadow: none; --stuic-phone-frame-radius: 0.5rem;"
|
|
242
|
+
{src}
|
|
243
|
+
{alt}
|
|
244
|
+
/>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Accessibility
|
|
248
|
+
|
|
249
|
+
- With `href` the frame is a link whose accessible name is the image's `alt` (or your
|
|
250
|
+
`children`'s text). Give it one: an empty `alt` on a linked frame is an unnamed link.
|
|
251
|
+
- Without `href` the frame is a plain container; the image is the only content a screen reader
|
|
252
|
+
sees, so a decorative screenshot can keep the default empty `alt`.
|
|
253
|
+
- The camera cutout is `aria-hidden` and ignores pointer events.
|
|
254
|
+
- Linked frames show a focus ring on `:focus-visible` (`--stuic-phone-frame-ring-*`).
|
|
255
|
+
|
|
256
|
+
## Theming
|
|
257
|
+
|
|
258
|
+
Only the screen background (`--stuic-color-background`) and the focus ring (`--stuic-color-ring`)
|
|
259
|
+
come from the theme; the bezel is a fixed near-black in both schemes, with a lighter hairline in
|
|
260
|
+
dark mode so it keeps an edge against dark page backgrounds.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
PHONE FRAME COMPONENT TOKENS
|
|
3
|
+
Override globally: :root { --stuic-phone-frame-bg: #e5e5e5; }
|
|
4
|
+
Override locally: <PhoneFrame style="--stuic-phone-frame-padding: 0.75rem;">
|
|
5
|
+
============================================================================ */
|
|
6
|
+
|
|
7
|
+
/* prettier-ignore */
|
|
8
|
+
:root {
|
|
9
|
+
/* Bezel */
|
|
10
|
+
--stuic-phone-frame-bg: #171717;
|
|
11
|
+
--stuic-phone-frame-padding: 0.5rem;
|
|
12
|
+
--stuic-phone-frame-radius: 1.75rem;
|
|
13
|
+
--stuic-phone-frame-border-color: rgb(0 0 0 / 0.4);
|
|
14
|
+
|
|
15
|
+
/* Screen */
|
|
16
|
+
--stuic-phone-frame-screen-bg: var(--stuic-color-background);
|
|
17
|
+
--stuic-phone-frame-screen-aspect-ratio: 390 / 844; /* iPhone 14 class, CSS px */
|
|
18
|
+
--stuic-phone-frame-screen-height: auto; /* a length = clipped window */
|
|
19
|
+
|
|
20
|
+
/* Dynamic-island cutout (notch="island"); sizes relative to the screen width */
|
|
21
|
+
--stuic-phone-frame-island-width: 32%;
|
|
22
|
+
--stuic-phone-frame-island-top: 3%;
|
|
23
|
+
|
|
24
|
+
/* Classic notch cutout (notch="notch"); sizes relative to the screen width */
|
|
25
|
+
--stuic-phone-frame-notch-width: 54%;
|
|
26
|
+
--stuic-phone-frame-notch-radius: 1rem;
|
|
27
|
+
|
|
28
|
+
/* Focus ring (linked frames) */
|
|
29
|
+
--stuic-phone-frame-ring-width: 3px;
|
|
30
|
+
--stuic-phone-frame-ring-offset: 2px;
|
|
31
|
+
--stuic-phone-frame-ring-color: var(--stuic-color-ring);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
:root.dark {
|
|
35
|
+
--stuic-phone-frame-border-color: rgb(255 255 255 / 0.15);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@layer components {
|
|
39
|
+
/* The bezel. Block-level so it fills its container: size it with `class`. */
|
|
40
|
+
.stuic-phone-frame {
|
|
41
|
+
display: block;
|
|
42
|
+
box-sizing: border-box;
|
|
43
|
+
padding: var(--stuic-phone-frame-padding);
|
|
44
|
+
background: var(--stuic-phone-frame-bg);
|
|
45
|
+
border-style: solid;
|
|
46
|
+
border-width: var(--stuic-phone-frame-border-width, var(--stuic-border-width));
|
|
47
|
+
border-color: var(--stuic-phone-frame-border-color);
|
|
48
|
+
border-radius: var(--stuic-phone-frame-radius);
|
|
49
|
+
box-shadow: var(--stuic-phone-frame-shadow, var(--stuic-shadow-dialog));
|
|
50
|
+
color: inherit;
|
|
51
|
+
text-decoration: none;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
a.stuic-phone-frame:focus-visible {
|
|
55
|
+
outline: var(--stuic-phone-frame-ring-width) solid var(--stuic-phone-frame-ring-color);
|
|
56
|
+
outline-offset: var(--stuic-phone-frame-ring-offset);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* The screen: a clipping window. `width: 100%` + `height: auto` lets `aspect-ratio`
|
|
60
|
+
derive the height; a length in --stuic-phone-frame-screen-height makes both axes
|
|
61
|
+
definite, so `aspect-ratio` is ignored and the window shows the top of the content
|
|
62
|
+
(see RATIO_LOCKED_FRAME.md, G14). The radius is concentric with the bezel's unless
|
|
63
|
+
overridden. */
|
|
64
|
+
.stuic-phone-frame-screen {
|
|
65
|
+
position: relative;
|
|
66
|
+
width: 100%;
|
|
67
|
+
height: var(--stuic-phone-frame-screen-height);
|
|
68
|
+
aspect-ratio: var(--stuic-phone-frame-screen-aspect-ratio);
|
|
69
|
+
overflow: hidden;
|
|
70
|
+
border-radius: var(
|
|
71
|
+
--stuic-phone-frame-screen-radius,
|
|
72
|
+
calc(
|
|
73
|
+
var(--stuic-phone-frame-radius) - var(--stuic-phone-frame-padding) -
|
|
74
|
+
var(--stuic-phone-frame-border-width, var(--stuic-border-width))
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
background: var(--stuic-phone-frame-screen-bg);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.stuic-phone-frame-image {
|
|
81
|
+
display: block;
|
|
82
|
+
width: 100%;
|
|
83
|
+
height: 100%;
|
|
84
|
+
object-fit: cover;
|
|
85
|
+
object-position: top;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Camera cutout, overlaid on the screen content. Width-relative so it keeps its
|
|
89
|
+
shape in a clipped (fixed-height) screen too: the height comes from aspect-ratio,
|
|
90
|
+
and the island's top offset is a margin (a % margin resolves against the
|
|
91
|
+
containing block's WIDTH, even vertically). */
|
|
92
|
+
.stuic-phone-frame-notch {
|
|
93
|
+
position: absolute;
|
|
94
|
+
top: 0;
|
|
95
|
+
left: 50%;
|
|
96
|
+
transform: translateX(-50%);
|
|
97
|
+
pointer-events: none;
|
|
98
|
+
background: var(--stuic-phone-frame-notch-bg, var(--stuic-phone-frame-bg));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.stuic-phone-frame[data-notch="island"] .stuic-phone-frame-notch {
|
|
102
|
+
width: var(--stuic-phone-frame-island-width);
|
|
103
|
+
aspect-ratio: 126 / 37;
|
|
104
|
+
margin-top: var(--stuic-phone-frame-island-top);
|
|
105
|
+
border-radius: 9999px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.stuic-phone-frame[data-notch="notch"] .stuic-phone-frame-notch {
|
|
109
|
+
width: var(--stuic-phone-frame-notch-width);
|
|
110
|
+
aspect-ratio: 209 / 30;
|
|
111
|
+
border-radius: 0 0 var(--stuic-phone-frame-notch-radius)
|
|
112
|
+
var(--stuic-phone-frame-notch-radius);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as PhoneFrame, type Props as PhoneFrameProps, type PhoneFrameNotch, } from "./PhoneFrame.svelte";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as PhoneFrame, } from "./PhoneFrame.svelte";
|
package/dist/index.css
CHANGED
|
@@ -119,6 +119,7 @@ In practice:
|
|
|
119
119
|
@import "./components/Notifications/index.css";
|
|
120
120
|
@import "./components/OtpInput/index.css";
|
|
121
121
|
@import "./components/Pagination/index.css";
|
|
122
|
+
@import "./components/PhoneFrame/index.css";
|
|
122
123
|
@import "./components/Pill/index.css";
|
|
123
124
|
@import "./components/PricingTable/index.css";
|
|
124
125
|
@import "./components/Progress/index.css";
|
package/dist/index.d.ts
CHANGED
|
@@ -75,6 +75,7 @@ export * from "./components/Nav/index.js";
|
|
|
75
75
|
export * from "./components/Notifications/index.js";
|
|
76
76
|
export * from "./components/OtpInput/index.js";
|
|
77
77
|
export * from "./components/Pagination/index.js";
|
|
78
|
+
export * from "./components/PhoneFrame/index.js";
|
|
78
79
|
export * from "./components/Pill/index.js";
|
|
79
80
|
export * from "./components/PricingTable/index.js";
|
|
80
81
|
export * from "./components/Progress/index.js";
|
package/dist/index.js
CHANGED
|
@@ -81,6 +81,7 @@ export * from "./components/Nav/index.js";
|
|
|
81
81
|
export * from "./components/Notifications/index.js";
|
|
82
82
|
export * from "./components/OtpInput/index.js";
|
|
83
83
|
export * from "./components/Pagination/index.js";
|
|
84
|
+
export * from "./components/PhoneFrame/index.js";
|
|
84
85
|
export * from "./components/Pill/index.js";
|
|
85
86
|
export * from "./components/PricingTable/index.js";
|
|
86
87
|
export * from "./components/Progress/index.js";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
84 Svelte 5 component directories with consistent API patterns. All use runes-based reactivity.
|
|
6
6
|
|
|
7
7
|
## Component Categories
|
|
8
8
|
|
|
@@ -118,6 +118,7 @@
|
|
|
118
118
|
| Tree | Hierarchical tree view with keyboard nav and drag-and-drop |
|
|
119
119
|
| X | Styled close/multiply SVG icon |
|
|
120
120
|
| ImageCycler | Auto-cycling image carousel with fade transitions and preloading |
|
|
121
|
+
| PhoneFrame | Phone-shaped bezel around a screenshot (or any content): link, clipped top window, notch, tokens |
|
|
121
122
|
|
|
122
123
|
### E-commerce
|
|
123
124
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marianmeres/stuic",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.194.0",
|
|
4
4
|
"packageManager": "pnpm@11.5.0",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite dev",
|
|
@@ -137,24 +137,24 @@
|
|
|
137
137
|
}
|
|
138
138
|
},
|
|
139
139
|
"devDependencies": {
|
|
140
|
-
"@codemirror/commands": "^6.11.
|
|
140
|
+
"@codemirror/commands": "^6.11.1",
|
|
141
141
|
"@codemirror/lang-markdown": "^6.5.2",
|
|
142
142
|
"@codemirror/language": "^6.12.4",
|
|
143
143
|
"@codemirror/language-data": "^6.5.2",
|
|
144
|
-
"@codemirror/state": "^6.7.
|
|
145
|
-
"@codemirror/view": "^6.43.
|
|
144
|
+
"@codemirror/state": "^6.7.6",
|
|
145
|
+
"@codemirror/view": "^6.43.13",
|
|
146
146
|
"@eslint/js": "^9.39.5",
|
|
147
147
|
"@marianmeres/random-human-readable": "^1.10.2",
|
|
148
148
|
"@marianmeres/trend-chart": "^0.5.0",
|
|
149
|
-
"@milkdown/core": "^7.22.
|
|
150
|
-
"@milkdown/ctx": "^7.22.
|
|
151
|
-
"@milkdown/plugin-history": "^7.22.
|
|
152
|
-
"@milkdown/plugin-listener": "^7.22.
|
|
153
|
-
"@milkdown/preset-commonmark": "^7.22.
|
|
154
|
-
"@milkdown/preset-gfm": "^7.22.
|
|
155
|
-
"@milkdown/prose": "^7.22.
|
|
156
|
-
"@milkdown/transformer": "^7.22.
|
|
157
|
-
"@milkdown/utils": "^7.22.
|
|
149
|
+
"@milkdown/core": "^7.22.2",
|
|
150
|
+
"@milkdown/ctx": "^7.22.2",
|
|
151
|
+
"@milkdown/plugin-history": "^7.22.2",
|
|
152
|
+
"@milkdown/plugin-listener": "^7.22.2",
|
|
153
|
+
"@milkdown/preset-commonmark": "^7.22.2",
|
|
154
|
+
"@milkdown/preset-gfm": "^7.22.2",
|
|
155
|
+
"@milkdown/prose": "^7.22.2",
|
|
156
|
+
"@milkdown/transformer": "^7.22.2",
|
|
157
|
+
"@milkdown/utils": "^7.22.2",
|
|
158
158
|
"@sveltejs/adapter-auto": "^4.0.0",
|
|
159
159
|
"@sveltejs/kit": "^2.70.3",
|
|
160
160
|
"@sveltejs/package": "^2.5.8",
|
|
@@ -163,28 +163,28 @@
|
|
|
163
163
|
"@tailwindcss/forms": "^0.5.11",
|
|
164
164
|
"@tailwindcss/typography": "^0.5.20",
|
|
165
165
|
"@tailwindcss/vite": "^4.3.3",
|
|
166
|
-
"@types/node": "^25.9.
|
|
166
|
+
"@types/node": "^25.9.9",
|
|
167
167
|
"@vitest/browser-playwright": "^4.1.11",
|
|
168
168
|
"dotenv": "^16.6.1",
|
|
169
169
|
"eslint": "^9.39.5",
|
|
170
170
|
"globals": "^16.5.0",
|
|
171
171
|
"playwright": "^1.63.0",
|
|
172
|
-
"prettier": "^3.9.
|
|
172
|
+
"prettier": "^3.9.9",
|
|
173
173
|
"prettier-plugin-svelte": "^3.5.2",
|
|
174
|
-
"publint": "^0.3.
|
|
175
|
-
"svelte": "^5.57.
|
|
174
|
+
"publint": "^0.3.25",
|
|
175
|
+
"svelte": "^5.57.2",
|
|
176
176
|
"svelte-check": "^4.7.6",
|
|
177
177
|
"tailwindcss": "^4.3.3",
|
|
178
|
-
"tsx": "^4.23.
|
|
178
|
+
"tsx": "^4.23.15",
|
|
179
179
|
"typescript": "^5.9.3",
|
|
180
|
-
"typescript-eslint": "^8.
|
|
181
|
-
"vite": "^7.3.
|
|
180
|
+
"typescript-eslint": "^8.71.1",
|
|
181
|
+
"vite": "^7.3.7",
|
|
182
182
|
"vitest": "^4.1.11",
|
|
183
183
|
"vitest-browser-svelte": "^2.2.1"
|
|
184
184
|
},
|
|
185
185
|
"dependencies": {
|
|
186
186
|
"@marianmeres/calendar-utils": "^2.0.0",
|
|
187
|
-
"@marianmeres/clog": "^3.
|
|
187
|
+
"@marianmeres/clog": "^3.23.0",
|
|
188
188
|
"@marianmeres/countries": "^1.1.0",
|
|
189
189
|
"@marianmeres/cron-parser": "^1.0.1",
|
|
190
190
|
"@marianmeres/design-tokens": "^1.20.0",
|
|
@@ -194,8 +194,8 @@
|
|
|
194
194
|
"@marianmeres/parse-boolean": "^2.1.0",
|
|
195
195
|
"@marianmeres/ticker": "^1.17.1",
|
|
196
196
|
"@marianmeres/tree": "^2.3.0",
|
|
197
|
-
"libphonenumber-js": "^1.13.
|
|
197
|
+
"libphonenumber-js": "^1.13.14",
|
|
198
198
|
"runed": "^0.23.4",
|
|
199
|
-
"tailwind-merge": "^3.
|
|
199
|
+
"tailwind-merge": "^3.7.0"
|
|
200
200
|
}
|
|
201
201
|
}
|