A list of options that can be selected by the user.
<script lang="ts"> import { Listbox } from "bits-ui"; import Check from "phosphor-svelte/lib/Check"; import Palette from "phosphor-svelte/lib/Palette"; import CaretUpDown from "phosphor-svelte/lib/CaretUpDown"; import CaretDoubleUp from "phosphor-svelte/lib/CaretDoubleUp"; import CaretDoubleDown from "phosphor-svelte/lib/CaretDoubleDown"; const themes = [ { value: "light-monochrome", label: "Light Monochrome" }, { value: "dark-green", label: "Dark Green" }, { value: "svelte-orange", label: "Svelte Orange" }, { value: "punk-pink", label: "Punk Pink" }, { value: "ocean-blue", label: "Ocean Blue" }, { value: "sunset-red", label: "Sunset Red" }, { value: "forest-green", label: "Forest Green" }, { value: "lavender-purple", label: "Lavender Purple" }, { value: "mustard-yellow", label: "Mustard Yellow" }, { value: "slate-gray", label: "Slate Gray" }, { value: "neon-green", label: "Neon Green" }, { value: "coral-reef", label: "Coral Reef" }, { value: "midnight-blue", label: "Midnight Blue" }, { value: "crimson-red", label: "Crimson Red" }, { value: "mint-green", label: "Mint Green" }, { value: "pastel-pink", label: "Pastel Pink" }, { value: "golden-yellow", label: "Golden Yellow" }, { value: "deep-purple", label: "Deep Purple" }, { value: "turquoise-blue", label: "Turquoise Blue" }, { value: "burnt-orange", label: "Burnt Orange" } ]; let value = $state<string>(""); const selectedLabel = $derived( value ? themes.find((theme) => theme.value === value)?.label : "Select a theme" ); </script> <Listbox.Root type="single" bind:value> <Listbox.Trigger class="inline-flex h-input w-[296px] select-none items-center rounded-9px border border-border-input bg-background px-[11px] text-sm transition-colors placeholder:text-foreground-alt/50" aria-label="Select a theme" > <Palette class="mr-[9px] size-6 text-muted-foreground" /> {selectedLabel} <CaretUpDown class="ml-auto size-6 text-muted-foreground" /> </Listbox.Trigger> <Listbox.Portal> <Listbox.Content class="max-h-96 w-[var(--bits-listbox-trigger-width)] min-w-[var(--bits-listbox-trigger-width)] rounded-xl border border-muted bg-background px-1 py-3 shadow-popover outline-none" sideOffset={10} > <Listbox.ScrollUpButton class="flex w-full items-center justify-center"> <CaretDoubleUp class="size-3" /> </Listbox.ScrollUpButton> <Listbox.Viewport class="p-1"> {#each themes as theme, i (i + theme.value)} <Listbox.Item class="flex h-10 w-full select-none items-center rounded-button py-3 pl-5 pr-1.5 text-sm capitalize outline-none duration-75 data-[highlighted]:bg-muted" value={theme.value} label={theme.label} > {#snippet children({ selected })} {theme.label} {#if selected} <div class="ml-auto"> <Check /> </div> {/if} {/snippet} </Listbox.Item> {/each} </Listbox.Viewport> <Listbox.ScrollDownButton class="flex w-full items-center justify-center"> <CaretDoubleDown class="size-3" /> </Listbox.ScrollDownButton> </Listbox.Content> </Listbox.Portal> </Listbox.Root>
import typography from "@tailwindcss/typography"; import animate from "tailwindcss-animate"; import { fontFamily } from "tailwindcss/defaultTheme"; /** @type {import('tailwindcss').Config} */ export default { darkMode: "class", content: ["./src/**/*.{html,js,svelte,ts}"], theme: { container: { center: true, screens: { "2xl": "1440px", }, }, extend: { colors: { border: { DEFAULT: "hsl(var(--border-card))", input: "hsl(var(--border-input))", "input-hover": "hsl(var(--border-input-hover))", }, background: { DEFAULT: "hsl(var(--background) / <alpha-value>)", alt: "hsl(var(--background-alt) / <alpha-value>)", }, foreground: { DEFAULT: "hsl(var(--foreground) / <alpha-value>)", alt: "hsl(var(--foreground-alt) / <alpha-value>)", }, muted: { DEFAULT: "hsl(var(--muted) / <alpha-value>)", foreground: "hsl(var(--muted-foreground))", }, dark: { DEFAULT: "hsl(var(--dark) / <alpha-value>)", 4: "hsl(var(--dark-04))", 10: "hsl(var(--dark-10))", 40: "hsl(var(--dark-40))", }, accent: { DEFAULT: "hsl(var(--accent) / <alpha-value>)", foreground: "hsl(var(--accent-foreground) / <alpha-value>)", }, destructive: { DEFAULT: "hsl(var(--destructive) / <alpha-value>)", }, contrast: { DEFAULT: "hsl(var(--contrast) / <alpha-value>)", }, }, fontFamily: { sans: ["Inter", ...fontFamily.sans], mono: ["Source Code Pro", ...fontFamily.mono], alt: ["Courier", ...fontFamily.sans], }, fontSize: { xxs: "10px", }, borderWidth: { 6: "6px", }, borderRadius: { card: "16px", "card-lg": "20px", "card-sm": "10px", input: "9px", button: "5px", "5px": "5px", "9px": "9px", "10px": "10px", "15px": "15px", }, height: { input: "3rem", "input-sm": "2.5rem", }, boxShadow: { mini: "var(--shadow-mini)", "mini-inset": "var(--shadow-mini-inset)", popover: "var(--shadow-popover)", kbd: "var(--shadow-kbd)", btn: "var(--shadow-btn)", card: "var(--shadow-card)", "date-field-focus": "var(--shadow-date-field-focus)", }, opacity: { 8: "0.08", }, scale: { 80: ".80", 98: ".98", 99: ".99", }, }, keyframes: { "accordion-down": { from: { height: "0" }, to: { height: "var(--bits-accordion-content-height)" }, }, "accordion-up": { from: { height: "var(--bits-accordion-content-height)" }, to: { height: "0" }, }, "caret-blink": { "0%,70%,100%": { opacity: "1" }, "20%,50%": { opacity: "0" }, }, }, animation: { "accordion-down": "accordion-down 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out", "caret-blink": "caret-blink 1.25s ease-out infinite", }, }, plugins: [typography, animate], };
@import url("https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap"); @tailwind base; @tailwind components; @tailwind utilities; @layer base { :root { /* Colors */ --background: 0 0% 100%; --background-alt: 0 0% 100%; --foreground: 0 0% 9%; --foreground-alt: 0 0% 32%; --muted: 240 5% 96%; --muted-foreground: 0 0% 9% / 0.4; --border: 240 6% 10%; --border-input: 240 6% 10% / 0.17; --border-input-hover: 240 6% 10% / 0.4; --border-card: 240 6% 10% / 0.1; --dark: 240 6% 10%; --dark-10: 240 6% 10% / 0.1; --dark-40: 240 6% 10% / 0.4; --dark-04: 240 6% 10% / 0.04; --accent: 204 94% 94%; --accent-foreground: 204 80% 16%; --destructive: 347 77% 50%; /* black */ --constrast: 0 0% 0%; /* Shadows */ --shadow-mini: 0px 1px 0px 1px rgba(0, 0, 0, 0.04); --shadow-mini-inset: 0px 1px 0px 0px rgba(0, 0, 0, 0.04) inset; --shadow-popover: 0px 7px 12px 3px hsla(var(--dark-10)); --shadow-kbd: 0px 2px 0px 0px rgba(0, 0, 0, 0.07); --shadow-btn: 0px 1px 0px 1px rgba(0, 0, 0, 0.03); --shadow-card: 0px 2px 0px 1px rgba(0, 0, 0, 0.04); --shadow-date-field-focus: 0px 0px 0px 3px rgba(24, 24, 27, 0.17); } .dark { /* Colors */ --background: 0 0% 5%; --background-alt: 0 0% 8%; --foreground: 0 0% 95%; --foreground-alt: 0 0% 70%; --muted: 240 4% 16%; --muted-foreground: 0 0% 100% / 0.4; --border: 0 0% 96%; --border-input: 0 0% 96% / 0.17; --border-input-hover: 0 0% 96% / 0.4; --border-card: 0 0% 96% / 0.1; --dark: 0 0% 96%; --dark-40: 0 0% 96% / 0.4; --dark-10: 0 0% 96% / 0.1; --dark-04: 0 0% 96% / 0.04; --accent: 204 90 90%; --accent-foreground: 204 94% 94%; --destructive: 350 89% 60%; /* white */ --constrast: 0 0% 100%; /* Shadows */ --shadow-mini: 0px 1px 0px 1px rgba(0, 0, 0, 0.3); --shadow-mini-inset: 0px 1px 0px 0px rgba(0, 0, 0, 0.5) inset; --shadow-popover: 0px 7px 12px 3px hsla(0deg 0% 0% / 30%); --shadow-kbd: 0px 2px 0px 0px rgba(255, 255, 255, 0.07); --shadow-btn: 0px 1px 0px 1px rgba(0, 0, 0, 0.2); --shadow-card: 0px 2px 0px 1px rgba(0, 0, 0, 0.4); --shadow-date-field-focus: 0px 0px 0px 3px rgba(244, 244, 245, 0.1); } } @layer base { * { @apply border-border; } html { -webkit-text-size-adjust: 100%; font-variation-settings: normal; } body { @apply bg-background text-foreground; font-feature-settings: "rlig" 1, "calt" 1; } /* Mobile tap highlight */ /* https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-tap-highlight-color */ html { -webkit-tap-highlight-color: rgba(128, 128, 128, 0.5); } ::selection { background: #fdffa4; color: black; } /* === Scrollbars === */ ::-webkit-scrollbar { @apply w-2; @apply h-2; } ::-webkit-scrollbar-track { @apply !bg-transparent; } ::-webkit-scrollbar-thumb { @apply rounded-card-lg !bg-dark-10; } ::-webkit-scrollbar-corner { background: rgba(0, 0, 0, 0); } /* Firefox */ /* https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color#browser_compatibility */ html { scrollbar-color: var(--bg-muted); } .antialised { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } } @layer utilities { .step { counter-increment: step; } .step:before { @apply absolute inline-flex h-9 w-9 items-center justify-center rounded-full border-4 border-background bg-muted text-center -indent-px font-mono text-base font-medium; @apply ml-[-50px] mt-[-4px]; content: counter(step); } } @layer components { *:not(body):not(.focus-override) { outline: none !important; &:focus-visible { @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground focus-visible:ring-offset-2 focus-visible:ring-offset-background; } } .link { @apply inline-flex items-center gap-1 rounded-sm font-medium underline underline-offset-4 hover:text-foreground/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground focus-visible:ring-offset-2 focus-visible:ring-offset-background; } input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } /* Firefox */ input[type="number"] { -moz-appearance: textfield; } }
<script lang="ts"> import { Listbox } from "bits-ui"; </script> <Listbox.Root> <Listbox.Label /> <Listbox.Content> <Listbox.Group> <Listbox.GroupLabel /> <Listbox.Item /> </Listbox.Group> </Listbox.Content> </Listbox.Root>