Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ Changelog
=========


## v10.1.5 *(2026-03-21)*
- New: skeleton loader components.
- New: icon `photo`.


## v10.1.4 *(2026-03-19)*
- Grid column cell renderer now accepts svelte snippets.

Expand Down
1 change: 1 addition & 0 deletions docs/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export { ColorPalette } from './color-palette';
export { Icon } from './icon';
export { Menu } from './menu';
export { Shortcut } from './shortcut';
export { Skeleton } from './skeleton';
export { Splitter } from './splitter';
export { Tag } from './tag';
export { ThemeSwitcher } from './theme-switcher';
Expand Down
4 changes: 4 additions & 0 deletions docs/components/skeleton/Skeleton.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

.skeleton-cards {
gap: 2rem;
}
138 changes: 138 additions & 0 deletions docs/components/skeleton/Skeleton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<h2>Skeleton</h2>

<p>Animated placeholder for loading states.</p>


<h3>Skeleton Shapes</h3>

<h4>Rectangle</h4>
<SkeletonRect width={200} height={100} />

<h4>Circle</h4>
<SkeletonCircle size={60} />

<h4>Text</h4>
<SkeletonText size={3} />

<h4>Image</h4>
<SkeletonImage />



<h3>Skeleton Layouts</h3>

<h4>SkeletonHStack</h4>
<SkeletonHStack>
<SkeletonCircle />
<SkeletonCircle />
<SkeletonCircle />
<SkeletonCircle />
</SkeletonHStack>


<h4>SkeletonVStack</h4>
<SkeletonVStack>
<SkeletonCircle />
<SkeletonCircle />
<SkeletonCircle />
</SkeletonVStack>


<h4>SkeletonGrid</h4>
<SkeletonGrid size={120}>
<SkeletonRect height={100} />
<SkeletonRect height={100} />
<SkeletonRect height={100} />
<SkeletonRect height={100} />
<SkeletonRect height={100} />
<SkeletonRect height={100} />
<SkeletonRect height={100} />
<SkeletonRect height={100} />
</SkeletonGrid>


<h3>Custom build</h3>

<h4>Cards list</h4>
<SkeletonVStack class="skeleton-cards">
<SkeletonHStack>
<SkeletonCircle />
<SkeletonText size={3} />
</SkeletonHStack>
<SkeletonHStack>
<SkeletonCircle />
<SkeletonText size={3} />
</SkeletonHStack>
</SkeletonVStack>


<h4>Cards grid</h4>
{#snippet card()}
<SkeletonVStack>
<SkeletonHStack>
<SkeletonImage width="3.9em" height="3.9em" />
<SkeletonText size={3} />
</SkeletonHStack>
<SkeletonText size={3} />
</SkeletonVStack>
{/snippet}

<SkeletonGrid class="skeleton-cards">
{@render card()}
{@render card()}
{@render card()}
{@render card()}
{@render card()}
{@render card()}
</SkeletonGrid>



<Code>{`
<SkeletonRect width={200} height={100} />
<SkeletonCircle width={60} />
<SkeletonText size={3} />
<SkeletonImage />

<SkeletonHStack>
<SkeletonCircle />
<SkeletonCircle />
<SkeletonCircle />
<SkeletonCircle />
</SkeletonHStack>

<SkeletonVStack>
<SkeletonCircle />
<SkeletonCircle />
<SkeletonCircle />
</SkeletonVStack>

<SkeletonGrid size={120}>
<SkeletonRect height={100} />
<SkeletonRect height={100} />
<SkeletonRect height={100} />
<SkeletonRect height={100} />
</SkeletonGrid>

`}</Code>


<API props={shapeProps} title="Skeleton Shape" description="" />

<script lang="ts">
import './Skeleton.css';
import type { ApiProp } from '../../api-table/types';
import { API, PROPS } from '../../api-table';
import { Code } from '../../code-example';

import { SkeletonRect, SkeletonCircle, SkeletonText, SkeletonImage, SkeletonHStack, SkeletonVStack, SkeletonGrid } from '../../../src';


const shapeProps = <ApiProp[]>[
PROPS.class,
{ name: 'size', type: 'number', description: 'Shape-specific count (lines of text / size of the circle).' },
{ name: 'width', type: 'number | string', description: 'Width of the shape. Can be a number (pixels) or a string (any CSS unit).' },
{ name: 'height', type: 'number | string', description: 'Height of the shape. Can be a number (pixels) or a string (any CSS unit).' },
];

</script>
1 change: 1 addition & 0 deletions docs/components/skeleton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Skeleton } from './Skeleton.svelte';
1 change: 1 addition & 0 deletions docs/nav/Nav.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
<NavItem name="Icon" {active} />
<NavItem name="Menu" {active} />
<NavItem name="Shortcut" {active} />
<NavItem name="Skeleton" {active} />
<NavItem name="Splitter" {active} />
<NavItem name="Tag" {active} />
<NavItem name="ThemeSwitcher" {active} />
Expand Down
5 changes: 5 additions & 0 deletions docs/pages/changelog.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<h1>Changelog</h1>
<h2>v10.1.5 <em>(2026-03-21)</em></h2>
<ul>
<li>New: skeleton loader components.</li>
<li>New: icon <code>photo</code>.</li>
</ul>
<h2>v10.1.4 <em>(2026-03-19)</em></h2>
<ul>
<li>Grid column cell renderer now accepts svelte snippets.</li>
Expand Down
1 change: 1 addition & 0 deletions src/icon/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const icons = {
meatballs: 'dots-vertical"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><circle cx="12" cy="12" r="1"/><circle cx="12" cy="19" r="1"/><circle cx="12" cy="5" r="1"',
minusCircle: 'circle-minus"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><circle cx="12" cy="12" r="9"/><line x1="9" y1="12" x2="15" y2="12"',
moon: 'moon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" ',
photo: 'photo"><path d="M15 8h.01" /><path d="M3 6a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3v-12z" /><path d="M3 16l5 -5c.928 -.893 2.072 -.893 3 0l5 5" /><path d="M14 14l1 -1c.928 -.893 2.072 -.893 3 0l3 3"',
plus: 'plus"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"',
plusCircle: 'circle-plus"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><circle cx="12" cy="12" r="9"/><line x1="9" y1="12" x2="15" y2="12"/><line x1="12" y1="9" x2="12" y2="15"',
print: 'printer"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M17 17h2a2 2 0 0 0 2 -2v-4a2 2 0 0 0 -2 -2h-14a2 2 0 0 0 -2 2v4a2 2 0 0 0 2 2h2" /><path d="M17 9v-4a2 2 0 0 0 -2 -2h-6a2 2 0 0 0 -2 2v4" /><path d="M7 13m0 2a2 2 0 0 1 2 -2h6a2 2 0 0 1 2 2v4a2 2 0 0 1 -2 2h-6a2 2 0 0 1 -2 -2z" ',
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from './panel';
export * from './popover';
export * from './push-button';
export * from './shortcut';
export * from './skeleton';
export * from './splitter';
export * from './table';
export * from './tag';
Expand Down
3 changes: 3 additions & 0 deletions src/skeleton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './layouts';
export * from './shapes';
export type * from './types';
30 changes: 30 additions & 0 deletions src/skeleton/layouts/Grid.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!--
@component ## SkeletonGrid

A grid layout for skeleton components.
Size is the minimum width of each column (default: 150px).
The grid will automatically fit as many columns as possible based on the specified size.

@example
```svelte
<SkeletonGrid size={150} class="custom-class"/>
```
-->

<div class={['skeleton-grid', className]}
style="grid-template-columns: repeat(auto-fill, minmax({minw}, 1fr));">
{@render children?.()}
</div>

<script lang="ts">
import type { SkeletonGridProps } from '../types';
import './layouts.css';

const {
class: className = '',
children = undefined,
size = 150,
}: SkeletonGridProps = $props();

const minw = $derived(typeof size === 'number' ? `${size}px` : size);
</script>
25 changes: 25 additions & 0 deletions src/skeleton/layouts/HStack.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!--
@component ## SkeletonHStack

A horizontal stack layout for skeleton components.

@example
```svelte
<SkeletonHStack class="custom-class"/>
```
-->

<div class={['skeleton-hstack', className]}>
{@render children?.()}
</div>

<script lang="ts">
import type { SkeletonContainer } from '../types';
import './layouts.css';

const {
class: className = '',
children = undefined,
}: SkeletonContainer = $props();

</script>
25 changes: 25 additions & 0 deletions src/skeleton/layouts/VStack.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!--
@component ## SkeletonVStack

A vertical stack layout for skeleton components.

@example
```svelte
<SkeletonVStack class="custom-class"/>
```
-->

<div class={['skeleton-vstack', className]}>
{@render children?.()}
</div>

<script lang="ts">
import type { SkeletonContainer } from '../types';
import './layouts.css';

const {
class: className = '',
children = undefined,
}: SkeletonContainer = $props();

</script>
3 changes: 3 additions & 0 deletions src/skeleton/layouts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as SkeletonHStack } from './HStack.svelte';
export { default as SkeletonVStack } from './VStack.svelte';
export { default as SkeletonGrid } from './Grid.svelte';
7 changes: 7 additions & 0 deletions src/skeleton/layouts/layouts.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.skeleton-hstack,
.skeleton-vstack { display: flex; gap: 0.7em; flex: 1; }

.skeleton-hstack { flex-flow: row nowrap; }
.skeleton-vstack { flex-flow: column nowrap; }

.skeleton-grid { display: grid; gap: 0.7em; }
26 changes: 26 additions & 0 deletions src/skeleton/shapes/Circle.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!--
@component ## SkeletonCircle

A single circular skeleton.
Perfect for avatar placeholders.

@example
```svelte
<SkeletonCircle size={80}/>
```
-->
<div class={cls} style:width style:height></div>

<script lang="ts">
import type { SkeletonCircleProps } from '../types';
import './shapes.css';

const {
class: className = '',
size = 60
}: SkeletonCircleProps = $props();

const width = $derived(typeof size === 'number' ? `${size}px` : size);
const height = $derived(typeof size === 'number' ? `${size}px` : size);
const cls = $derived(['skeleton-shape', 'skeleton-circle', className]);
</script>
29 changes: 29 additions & 0 deletions src/skeleton/shapes/Image.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!--
@component ## SkeletonImage

A single rounded rectangle sized for image placeholders.
Has a photo icon centered in the middle.

@example
```svelte
<SkeletonImage width="100%" height={120}/>
```
-->

<Rect class={['skeleton-img', className]} {width} {height}>
<Icon name="photo"/>
</Rect>

<script lang="ts">
import type { SkeletonImageProps } from '../types';
import './shapes.css';
import { Icon } from '../../icon';
import Rect from './Rect.svelte';

const {
class: className = '',
width = '100%',
height = 120,
}: SkeletonImageProps = $props();

</script>
30 changes: 30 additions & 0 deletions src/skeleton/shapes/Rect.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!--
@component ## SkeletonRect

Rectangular skeleton placeholder.
Perfect for blocks, buttons, or any rectangular element.

@example
```svelte
<SkeletonRect width={200} height={20} />
```
-->

<div class={cls} style:width={w} style:height={h}>{@render children?.()}</div>

<script lang="ts">
import type { SkeletonRectProps } from '../types';
import './shapes.css';


const {
class: className = '',
width = undefined,
height = undefined,
children = undefined,
}: SkeletonRectProps = $props();

const w = $derived(typeof width === 'number' ? `${width}px` : width);
const h = $derived(typeof height === 'number' ? `${height}px` : height);
const cls = $derived(['skeleton-shape', 'skeleton-rect', className]);
</script>
Loading