Skip to content

Commit

Permalink
feat(buttons): adds shallow styling for buttons & tabs
Browse files Browse the repository at this point in the history
  • Loading branch information
dvcol committed Nov 10, 2024
1 parent 1fd438a commit 3bbb5d7
Show file tree
Hide file tree
Showing 13 changed files with 173 additions and 84 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,25 @@
Neomorphic ui library for svelte 5

## TODO
- [x] Buttons
- @media any-pointer:coarse any-hover:none
- shallow
- [x] Tabs
- @media any-pointer:coarse any-hover:none
- paper ?
- card
- border loading
- skeleton
- images
- videos
- carousel
- closable
- hoverable
- action
- header/footer/buttons
- separator
- elevation
- shallow (raise on hover)
- avatar
- badge
- tags
Expand Down
24 changes: 18 additions & 6 deletions demo/components/DemoButtonGroups.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
import NeoButtonGroup from '~/buttons/NeoButtonGroup.svelte';
import IconAccount from '~/icons/IconAccount.svelte';
const { onClick, loading: loading$, onLoading, skeleton: skeleton$, onSkeleton } = useButtonState('DemoGroupClicked');
const { onClick, loading: loading$, onLoading } = useButtonState('DemoGroupClicked');
const loading = $derived.by(loading$);
const skeleton = $derived.by(skeleton$);
const options = $state({ shallow: false, skeleton: false });
const { matches } = useWatchMedia('(max-width: 1550px)');
const vertical = $derived.by(matches);
Expand All @@ -42,15 +42,23 @@
<NeoButton {loading} onclick={onLoading} {icon} />
<NeoButton onclick={onClick} {icon}>Icon</NeoButton>
<NeoButton reverse onclick={onClick} {icon}>Reversed</NeoButton>
<NeoButton {skeleton} onclick={onSkeleton}>Skeleton</NeoButton>
{/snippet}

{#snippet group(props: NeoButtonGroupContext = {})}
<NeoButtonGroup {vertical} {skeleton} {...props}>
<NeoButtonGroup {...options} {vertical} {...props}>
{@render buttons()}
</NeoButtonGroup>
{/snippet}

<div class="row">
<div class="column">
<NeoButtonGroup>
<NeoButton toggle bind:checked={options.shallow}>Shallow</NeoButton>
<NeoButton toggle bind:checked={options.skeleton}>Skeleton</NeoButton>
</NeoButtonGroup>
</div>
</div>

<div class="row">
{#each columns as { label, props }}
<div class="column">
Expand All @@ -66,14 +74,14 @@

<div class="column">
<span class="label">Pulse</span>
<NeoButtonGroup {skeleton} pulse>
<NeoButtonGroup {...options} pulse>
<NeoButton onclick={onClick}>Button</NeoButton>
<NeoButton toggle onclick={onClick}>Toggle</NeoButton>
<NeoButton {loading} onclick={onLoading} {icon} />
</NeoButtonGroup>

<span class="label">Coalesce</span>
<NeoButtonGroup {skeleton} coalesce>
<NeoButtonGroup {...options} coalesce>
<NeoButton onclick={onClick}>Button</NeoButton>
<NeoButton disabled onclick={onClick}>Disabled</NeoButton>
<NeoButton {loading} onclick={onLoading}>Loading</NeoButton>
Expand All @@ -90,6 +98,10 @@
.row {
@include flex.row($gap: var(--gap-xl));
align-items: center;
justify-content: center;
margin: 2rem 0;
}
@media (width > 1550px) {
Expand Down
74 changes: 44 additions & 30 deletions demo/components/DemoButtons.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@
import type { NeoButtonProps } from '~/buttons/neo-button.model';
import NeoButton from '~/buttons/NeoButton.svelte';
import NeoButtonGroup from '~/buttons/NeoButtonGroup.svelte';
import IconAccount from '~/icons/IconAccount.svelte';
const { onClick, loading: loading$, onLoading, skeleton: skeleton$, onSkeleton } = useButtonState('DemoButtonClick');
const loading = $derived.by(loading$);
const skeleton = $derived.by(skeleton$);
const { onClick, loading: isLoading, onLoading } = useButtonState('DemoButtonClick');
const options = $state({ disabled: false, shallow: false, skeleton: false, loading: false });
const loading = $derived.by(() => isLoading() || options.loading);
const columns = [
{ label: 'Default' },
{ label: 'Rounded', props: { rounded: true } },
{ label: 'Flat', props: { flat: true } },
{ label: 'Text', props: { text: true } },
{ label: 'Rounded', props: { rounded: true, shallow: true } },
{ label: 'Flat', props: { flat: true, shallow: true } },
{ label: 'Text', props: { text: true, shallow: true } },
];
</script>

Expand All @@ -28,19 +31,29 @@
{/snippet}

{#snippet buttons(opts: NeoButtonProps = {})}
<NeoButton {...opts} onclick={onClick} href={`${Path.ButtonGroups}`} use={link}>Anchor</NeoButton>
<NeoButton {...opts} onclick={onClick}>Button</NeoButton>
<NeoButton {...opts} toggle onclick={onClick}>Toggle</NeoButton>
<NeoButton {...opts} disabled onclick={onClick}>Disabled</NeoButton>
<NeoButton {...opts} {loading} onclick={onLoading}>Loading</NeoButton>
<NeoButton {...opts} {loading} onclick={onLoading} {icon} />
<NeoButton {...opts} onclick={onClick} {icon}>Icon</NeoButton>
<NeoButton {...opts} reverse onclick={onClick} {icon}>Reversed</NeoButton>
<NeoButton {...opts} {loading} pulse onclick={onLoading}>Pulse</NeoButton>
<NeoButton {...opts} coalesce onclick={onClick}>Coalesce</NeoButton>
<NeoButton {...opts} {skeleton} onclick={onSkeleton}>Skeleton</NeoButton>
<NeoButton {...opts} {...options} onclick={onClick} href={`${Path.ButtonGroups}`} use={link}>Anchor</NeoButton>
<NeoButton {...opts} {...options} onclick={onClick}>Button</NeoButton>
<NeoButton {...opts} {...options} toggle onclick={onClick}>Toggle</NeoButton>
<NeoButton {...opts} {...options} disabled onclick={onClick}>Disabled</NeoButton>
<NeoButton {...opts} {...options} {loading} onclick={onLoading}>Loading</NeoButton>
<NeoButton {...opts} {...options} {loading} onclick={onLoading} {icon} />
<NeoButton {...opts} {...options} onclick={onClick} {icon}>Icon</NeoButton>
<NeoButton {...opts} {...options} reverse onclick={onClick} {icon}>Reversed</NeoButton>
<NeoButton {...opts} {...options} {loading} pulse onclick={onLoading}>Pulse</NeoButton>
<NeoButton {...opts} {...options} coalesce onclick={onClick}>Coalesce</NeoButton>
{/snippet}

<div class="row">
<div class="column">
<NeoButtonGroup>
<NeoButton toggle bind:checked={options.disabled}>Disabled</NeoButton>
<NeoButton toggle bind:checked={options.loading}>Loading</NeoButton>
<NeoButton toggle bind:checked={options.shallow}>Shallow</NeoButton>
<NeoButton toggle bind:checked={options.skeleton}>Skeleton</NeoButton>
</NeoButtonGroup>
</div>
</div>

<div class="row">
{#each columns as { label, props }}
<div class="column">
Expand All @@ -52,37 +65,34 @@
<div class="column">
<span class="label">Glass</span>
<SphereBackdrop>
<NeoButton glass onclick={onClick} href={`${Path.ButtonGroups}`} use={link}>Anchor</NeoButton>
<NeoButton glass {...options} onclick={onClick} href={`${Path.ButtonGroups}`} use={link}>Anchor</NeoButton>
</SphereBackdrop>
<SphereBackdrop>
<NeoButton glass onclick={onClick}>Button</NeoButton>
<NeoButton glass {...options} onclick={onClick}>Button</NeoButton>
</SphereBackdrop>
<SphereBackdrop>
<NeoButton glass toggle onclick={onClick}>Toggle</NeoButton>
<NeoButton glass {...options} toggle onclick={onClick}>Toggle</NeoButton>
</SphereBackdrop>
<SphereBackdrop>
<NeoButton glass disabled onclick={onClick}>Disabled</NeoButton>
<NeoButton glass {...options} disabled onclick={onClick}>Disabled</NeoButton>
</SphereBackdrop>
<SphereBackdrop>
<NeoButton glass {loading} onclick={onLoading}>Loading</NeoButton>
<NeoButton glass {...options} {loading} onclick={onLoading}>Loading</NeoButton>
</SphereBackdrop>
<SphereBackdrop>
<NeoButton glass {loading} onclick={onLoading} {icon} />
<NeoButton glass {...options} {loading} onclick={onLoading} {icon} />
</SphereBackdrop>
<SphereBackdrop>
<NeoButton glass onclick={onClick} {icon}>Icon</NeoButton>
<NeoButton glass {...options} onclick={onClick} {icon}>Icon</NeoButton>
</SphereBackdrop>
<SphereBackdrop>
<NeoButton glass reverse onclick={onClick} {icon}>Reversed</NeoButton>
<NeoButton glass {...options} reverse onclick={onClick} {icon}>Reversed</NeoButton>
</SphereBackdrop>
<SphereBackdrop>
<NeoButton glass {loading} pulse onclick={onLoading}>Pulse</NeoButton>
<NeoButton glass {...options} {loading} pulse onclick={onLoading}>Pulse</NeoButton>
</SphereBackdrop>
<SphereBackdrop>
<NeoButton glass coalesce onclick={onClick}>Coalesce</NeoButton>
</SphereBackdrop>
<SphereBackdrop>
<NeoButton glass {skeleton} onclick={onSkeleton}>Skeleton</NeoButton>
<NeoButton glass {...options} coalesce onclick={onClick}>Coalesce</NeoButton>
</SphereBackdrop>
</div>
</div>
Expand All @@ -96,6 +106,10 @@
.row {
@include flex.row($gap: var(--gap-xl));
align-items: center;
justify-content: center;
margin: 2rem 0;
}
@media (width > 1550px) {
Expand Down
3 changes: 2 additions & 1 deletion demo/components/DemoTabs.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
const onClear = () => onChange();
const options = $state({ disabled: false, close: true, add: true, slide: true });
const options = $state({ disabled: false, close: true, add: true, slide: true, shallow: false });
const columns = [
{ label: 'Default' },
Expand Down Expand Up @@ -86,6 +86,7 @@
<div class="column">
<NeoButtonGroup>
<NeoButton toggle bind:checked={options.disabled}>Disabled</NeoButton>
<NeoButton toggle bind:checked={options.shallow}>Shallow</NeoButton>
<NeoButton toggle bind:checked={options.add}>Add</NeoButton>
<NeoButton toggle bind:checked={options.close}>Close</NeoButton>
<NeoButton toggle bind:checked={options.slide}>Slide</NeoButton>
Expand Down
11 changes: 0 additions & 11 deletions demo/utils/use-button-state.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,9 @@ export const useButtonState = (prefix = '') => {
onClick(e);
};

let skeleton = $state(false);
const onSkeleton = (e: MouseEvent, checked?: boolean, duration = 5000) => {
skeleton = !skeleton;
setTimeout(() => {
skeleton = !skeleton;
}, duration);
onClick(e);
};

return {
onClick,
loading: () => loading,
skeleton: () => skeleton,
onLoading,
onSkeleton,
};
};
65 changes: 52 additions & 13 deletions src/lib/buttons/NeoButton.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
text,
flat,
glass,
shallow,
rounded,
reverse,
coalesce,
Expand Down Expand Up @@ -118,6 +119,7 @@
class:glass
class:text
class:flat
class:shallow
class:rounded
class:rotate
class:empty
Expand Down Expand Up @@ -185,11 +187,17 @@
}
&.flat,
&.text {
&.text,
&.shallow {
--coalesce-box-shadow: var(--box-shadow-raised-1);
--pulse-box-shadow: var(--box-shadow-raised-1);
}
&.shallow {
--coalesce-box-shadow-reverse: var(--box-shadow-inset-0);
--pulse-box-shadow-reverse: var(--box-shadow-inset-0);
}
&.glass {
background-color: var(--glass-background-color);
box-shadow: var(--glass-box-shadow-raised-2);
Expand All @@ -204,6 +212,10 @@
&:active {
box-shadow: var(--glass-box-shadow-inset-1);
backdrop-filter: var(--blur-2);
&.shallow {
box-shadow: var(--glass-box-shadow-flat);
}
}
&:not(:hover, :active, &.pressed, &.skeleton) {
Expand All @@ -217,6 +229,11 @@
border-color: var(--neo-btn-border-color-hover, var(--glass-border-color-hover));
box-shadow: var(--box-shadow-flat);
backdrop-filter: var(--blur-3);
&.shallow {
border-color: transparent;
box-shadow: var(--glass-box-shadow-raised-1);
}
}
}
Expand All @@ -234,16 +251,6 @@
}
}
&:focus-visible {
color: var(--neo-btn-text-color-focused, var(--text-color-focused));
outline: none;
box-shadow: var(--box-shadow-raised-1);
&.flat:not(:active, :hover, &.pressed) {
border-color: var(--neo-btn-border-color-focused, var(--border-color-focused));
}
}
&.pressed,
&:active {
color: var(--neo-btn-text-color-active, var(--text-color-active));
Expand All @@ -256,9 +263,33 @@
backdrop-filter 0.3s ease,
box-shadow 0.15s ease-out;
&:focus-visible {
&.shallow {
box-shadow: var(--box-shadow-flat);
&.flat,
&.text {
box-shadow: var(--box-shadow-inset-1);
}
&:not(.text, .flat, .glass, .loading) {
border-color: var(--neo-btn-border-color, var(--border-color));
}
}
}
&:focus-visible {
color: var(--neo-btn-text-color-focused, var(--text-color-focused));
outline: none;
box-shadow: var(--box-shadow-raised-1);
&.pressed,
&:active {
color: var(--neo-btn-text-color-focused-active, var(--text-color-focused-active));
}
&.flat:not(:active, :hover, &.pressed) {
border-color: var(--neo-btn-border-color-focused, var(--border-color-focused));
}
}
&.start {
Expand All @@ -277,16 +308,24 @@
&:hover:not(:active, &.pressed) {
box-shadow: var(--box-shadow-flat);
&:not(.text, .glass, .flat:hover, .flat:focus-visible) {
&:not(.text, .glass, .flat:hover, .flat:focus-visible, .shallow:not(.flat)) {
border-color: var(--neo-btn-border-color-hover, var(--border-color));
}
&.shallow:not(.text, .flat, .glass) {
box-shadow: var(--box-shadow-raised-1);
}
}
&.text.loading:active:not(.pressed),
&.flat.loading:active:not(.pressed),
&.text:hover:not(:active, &.pressed),
&.flat:hover:not(:active, &.pressed) {
box-shadow: var(--box-shadow-inset-1);
&.shallow {
box-shadow: var(--box-shadow-inset-0);
}
}
&.skeleton {
Expand Down
Loading

0 comments on commit 3bbb5d7

Please sign in to comment.