Action List
action-list.svelteAction lists present a list of actions or options to the user.
Demo
Usage
001 <script lang="ts">
002 import ActionList from '$lib/components/action-list.svelte';
003 </script>
004
005 <div class="flex flex-col w-full">
006 <ActionList icon=">" href="#getting-started">Getting Started</ActionList>
007 <ActionList icon=">" href="#components">Components</ActionList>
008 <ActionList icon=">" href="#theming">Theming</ActionList>
009 </div>
010
action-list.svelte
001 <script lang="ts" module>
002 import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
003
004 /**
005 * Polymorphic: renders `<a>` when `href` is set, `<button>` otherwise. `href`
006 * discriminates the allowed attributes. `icon` is a short string (often one
007 * character like `>` or `=`) shown in the leading gutter.
008 */
009 export type ActionListProps =
010 | (HTMLButtonAttributes & { ref?: HTMLElement | null; icon?: string; href?: undefined })
011 | (HTMLAnchorAttributes & { ref?: HTMLElement | null; icon?: string; href: string });
012 </script>
013
014 <script lang="ts">
015 let {
016 class: className,
017 ref = $bindable(null),
018 icon = '',
019 href,
020 children,
021 ...restProps
022 }: ActionListProps = $props();
023 </script>
024
025 {#if href}
026 <a
027 bind:this={ref}
028 class={`item ${className ?? ''}`}
029 {href}
030 {...restProps as HTMLAnchorAttributes}
031 >
032 <span class="icon">{icon}</span>
033 <span class="text">
034 {@render children?.()}
035 </span>
036 </a>
037 {:else}
038 <button bind:this={ref} class={`item ${className ?? ''}`} {...restProps as HTMLButtonAttributes}>
039 <span class="icon">{icon}</span>
040 <span class="text">
041 {@render children?.()}
042 </span>
043 </button>
044 {/if}
045
046 <style>
047 .item {
048 align-items: flex-start;
049 background: transparent;
050 color: var(--text-primary);
051 cursor: pointer;
052 display: flex;
053 justify-content: space-between;
054 outline: 0;
055 border: 0;
056 text-decoration: none;
057
058 &:visited {
059 background: transparent;
060 color: var(--text-primary);
061 }
062
063 &:hover {
064 background: transparent;
065 color: var(--text-primary);
066 }
067
068 &:hover .icon {
069 background: var(--focus-ring);
070 }
071
072 &:focus-visible .icon {
073 background: var(--focus-ring);
074 }
075 }
076
077 .icon {
078 align-items: center;
079 background: var(--border-muted);
080 display: inline-flex;
081 flex-shrink: 0;
082 height: var(--line);
083 justify-content: center;
084 width: 3ch;
085 user-select: none;
086 }
087
088 .text {
089 align-items: center;
090 align-self: stretch;
091 background: var(--border-default);
092 display: inline-flex;
093 justify-content: flex-start;
094 min-width: 10%;
095 padding: 0 1ch 0 1ch;
096 user-select: none;
097 width: 100%;
098 }
099 </style>
100