@leodamours/ds-components
v0.3.1
Published
Vue 3 component library with built-in design tokens, theming via CSS custom properties, and algorithmic palette generation.
Downloads
78
Readme
@leodamours/ds-components
Vue 3 component library with built-in design tokens, theming via CSS custom properties, and algorithmic palette generation.
Install
npm install @leodamours/ds-componentsPeer dependencies: vue ^3.5.0
Optional: @leodamours/ds-icons — 1,920 Lucide-based icon components that pair with this library.
Setup
// main.ts
import '@leodamours/ds-components/style.css'The stylesheet bundles the default light theme, dark theme ([data-theme="dark"]), and all component styles. No additional CSS imports needed.
Usage
<script setup>
import { DsButton, DsInput } from '@leodamours/ds-components'
</script>
<template>
<DsInput label="Email" placeholder="[email protected]" />
<DsButton variant="primary" label="Submit" />
</template>Components
| Component | Description |
|---|---|
| DsAvatar | User avatar with image or initials fallback |
| DsBadge | Status badge / label |
| DsButton | Button with variants: primary, secondary, outline, ghost, danger, success |
| DsCard | Container card with variants: default, outlined, elevated |
| DsCheckbox | Checkbox input |
| DsDropdownSelect | Dropdown select with floating UI positioning |
| DsExpandableCard | Collapsible card with title, supports v-model for expand state |
| DsInput | Text input with label, help text, error state |
| DsLoadingSpinner | Animated loading spinner |
| DsModal | Modal dialog with backdrop |
| DsRadio | Radio input |
| DsSelect | Native select wrapper |
| DsSlider | Range slider with optional value display |
| DsSwitch | Toggle switch for boolean settings |
| DsTable | Data table with sorting and pagination |
| DsTabs | Tab navigation |
| DsTextarea | Multiline text input |
| DsToast / DsToastContainer | Toast notifications (see Toast system below) |
| DsTooltip | Tooltip on hover |
| DsUnderConstruction | Placeholder for pages not yet built |
All components export their prop types (e.g., DsButtonProps, DsCardProps).
Component props
DsAvatar
| Prop | Type | Default | Description |
|---|---|---|---|
| src | string | — | Image URL |
| alt | string | — | Alt text for image |
| name | string | — | Full name (used for initials fallback) |
| size | 'sm' \| 'md' \| 'lg' \| 'xl' | 'md' | Avatar size |
| variant | 'brand' \| 'neutral' | 'brand' | Color variant for initials background |
DsBadge
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | — | Badge text (can also use default slot) |
| variant | 'brand' \| 'neutral' \| 'error' \| 'success' \| 'warning' \| 'info' \| 'processing' | 'neutral' | Color variant |
| size | 'sm' \| 'md' \| 'lg' | 'md' | Badge size |
DsButton
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | — | Button text (can also use default slot) |
| variant | 'primary' \| 'secondary' \| 'outline' \| 'ghost' \| 'danger' \| 'success' \| 'warning' | 'primary' | Visual style |
| size | 'small' \| 'medium' \| 'large' | 'medium' | Button size |
| htmlType | 'button' \| 'submit' \| 'reset' | 'button' | Native button type |
| disabled | boolean | false | Disable interaction |
| loading | boolean | false | Show loading spinner |
| fullWidth | boolean | false | Expand to fill container |
| iconLeft | Component | — | Vue component icon on the left |
| iconRight | Component | — | Vue component icon on the right |
| ariaLabel | string | — | Accessible label override |
DsCard
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | 'default' \| 'outlined' \| 'elevated' | 'default' | Card style |
| noPadding | boolean | false | Remove inner padding |
Default slot for card content.
DsCheckbox
| Prop | Type | Default | Description |
|---|---|---|---|
| modelValue | boolean | false | Checked state (v-model) |
| label | string | — | Checkbox label |
| id | string | auto | HTML id attribute |
| value | string \| number \| boolean | — | Form value |
| disabled | boolean | false | Disable interaction |
| size | 'small' \| 'medium' \| 'large' | 'medium' | Checkbox size |
DsDropdownSelect
| Prop | Type | Default | Description |
|---|---|---|---|
| modelValue | string \| number | — | Selected value (v-model) |
| options | (string \| number \| { label, value, disabled? })[] | [] | List of options |
| placeholder | string | 'Select an option' | Placeholder text |
| variant | 'primary' \| 'secondary' \| 'ghost' | 'primary' | Trigger button style |
| size | 'small' \| 'medium' \| 'large' | 'medium' | Trigger button size |
| disabled | boolean | false | Disable interaction |
| loading | boolean | false | Show loading state |
| iconLeft | Component | — | Icon in the trigger button |
| fullWidth | boolean | false | Expand to fill container |
| emptyText | string | 'No options available' | Text shown when options list is empty |
DsExpandableCard
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | required | Card header text |
| modelValue | boolean | — | Controlled expand state (v-model) |
| defaultExpanded | boolean | false | Initial expand state (uncontrolled) |
| variant | 'default' \| 'outlined' \| 'elevated' | 'default' | Card style |
Default slot for expandable content.
DsInput
| Prop | Type | Default | Description |
|---|---|---|---|
| modelValue | string \| number | — | Input value (v-model) |
| label | string | — | Label text |
| type | 'text' \| 'email' \| 'password' \| 'number' \| 'tel' \| 'url' \| 'search' | 'text' | Input type |
| placeholder | string | — | Placeholder text |
| helpText | string | — | Help text below the input |
| error | string | — | Error message (replaces help text) |
| size | 'small' \| 'medium' \| 'large' | 'medium' | Input size |
| disabled | boolean | false | Disable interaction |
| readonly | boolean | false | Read-only state |
| required | boolean | false | Mark as required |
| clearable | boolean | false | Show clear button when filled |
| iconLeft | Component | — | Icon on the left |
| iconRight | Component | — | Icon on the right |
| autocomplete | string | — | HTML autocomplete attribute |
| maxLength | number | — | Character limit |
| min | number \| string | — | Minimum value (number input) |
| max | number \| string | — | Maximum value (number input) |
| step | number \| string | — | Step increment (number input) |
DsLoadingSpinner
| Prop | Type | Default | Description |
|---|---|---|---|
| size | 'small' \| 'medium' \| 'large' | 'medium' | Spinner size |
| variant | 'primary' \| 'secondary' \| 'white' \| 'current' | 'primary' | Color variant |
DsModal
| Prop | Type | Default | Description |
|---|---|---|---|
| modelValue | boolean | required | Open state (v-model) |
| title | string | — | Modal title |
| size | 'sm' \| 'md' \| 'lg' \| 'xl' | 'md' | Modal width |
| closable | boolean | true | Show close button |
| persistent | boolean | false | Prevent closing on backdrop click |
Slots: default (body), header, footer.
DsRadio
| Prop | Type | Default | Description |
|---|---|---|---|
| modelValue | string \| number \| boolean | — | Selected value (v-model) |
| label | string | — | Radio label |
| id | string | auto | HTML id attribute |
| name | string | — | Radio group name |
| value | string \| number \| boolean | — | This radio's value |
| disabled | boolean | false | Disable interaction |
| size | 'small' \| 'medium' \| 'large' | 'medium' | Radio size |
DsSelect
| Prop | Type | Default | Description |
|---|---|---|---|
| modelValue | string \| number | — | Selected value (v-model) |
| label | string | — | Label text |
| placeholder | string | — | Placeholder text |
| helpText | string | — | Help text below the select |
| error | string | — | Error message |
| size | 'small' \| 'medium' \| 'large' | 'medium' | Select size |
| disabled | boolean | false | Disable interaction |
| required | boolean | false | Mark as required |
| options | DsSelectOption[] | required | { label: string, value: string \| number, disabled?: boolean } |
DsSlider
| Prop | Type | Default | Description |
|---|---|---|---|
| modelValue | number | 0 | Slider value (v-model) |
| min | number | 0 | Minimum value |
| max | number | 100 | Maximum value |
| step | number | 1 | Step increment |
| disabled | boolean | false | Disable interaction |
| label | string | — | Label text |
| showValue | boolean | false | Display current value |
DsSwitch
| Prop | Type | Default | Description |
|---|---|---|---|
| modelValue | boolean | false | Checked state (v-model) |
| label | string | — | Label text |
| id | string | auto | HTML id attribute |
| disabled | boolean | false | Disable interaction |
| size | 'small' \| 'medium' \| 'large' | 'medium' | Switch size |
| labelPosition | 'left' \| 'right' | 'right' | Label placement |
DsTable
| Prop | Type | Default | Description |
|---|---|---|---|
| columns | DsTableColumn[] | required | Column definitions (see below) |
| rows | Record<string, any>[] | required | Row data |
| sortBy | string | — | Active sort column key |
| sortOrder | 'asc' \| 'desc' | — | Sort direction |
| loading | boolean | false | Show loading state |
| emptyText | string | 'No data available' | Text when no rows |
| striped | boolean | false | Alternate row shading |
| stickyHeader | boolean | false | Sticky column headers |
| pagination | DsTablePagination | — | Enable pagination (see below) |
| nextPageLabel | string | 'Next' | Pagination next label |
| previousPageLabel | string | 'Previous' | Pagination previous label |
| ofLabel | string | — | Pagination "of" label |
DsTableColumn: { key: string, label: string, sortable?: boolean, width?: string, align?: 'left' | 'center' | 'right' }
DsTablePagination: { page: number, perPage: number, total: number }
Column cells can be customized via named slots: #cell-{key}="{ row, column }".
DsTabs
| Prop | Type | Default | Description |
|---|---|---|---|
| modelValue | string | required | Active tab key (v-model) |
| tabs | DsTab[] | required | Tab definitions |
| variant | 'line' \| 'pill' | 'line' | Tab style |
| ariaLabel | string | 'Tabs' | Accessible label |
DsTab: { key: string, label: string, disabled?: boolean }
Tab content is rendered via named slots: #tab-{key}.
DsTextarea
| Prop | Type | Default | Description |
|---|---|---|---|
| modelValue | string | — | Textarea value (v-model) |
| label | string | — | Label text |
| placeholder | string | — | Placeholder text |
| helpText | string | — | Help text below the textarea |
| error | string | — | Error message |
| size | 'small' \| 'medium' \| 'large' | 'medium' | Textarea size |
| disabled | boolean | false | Disable interaction |
| readonly | boolean | false | Read-only state |
| required | boolean | false | Mark as required |
| rows | number | 3 | Visible rows |
| maxLength | number | — | Character limit (shows counter) |
DsTooltip
| Prop | Type | Default | Description |
|---|---|---|---|
| content | string | required | Tooltip text |
| placement | Placement | 'top' | Position ('top', 'bottom', 'left', 'right', etc.) |
| disabled | boolean | false | Disable tooltip |
Wrap any element in <DsTooltip> — the default slot is the trigger.
DsUnderConstruction
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | 'Under Construction' | Heading text |
| description | string | 'This page is under construction...' | Body text |
Toast system
Place <DsToastContainer /> once at the root of your app, then trigger toasts from anywhere via the useToast composable:
<!-- App.vue -->
<script setup>
import { DsToastContainer } from '@leodamours/ds-components'
</script>
<template>
<router-view />
<DsToastContainer />
</template>// Any component or composable
import { useToast } from '@leodamours/ds-components'
const toast = useToast()
toast.success('Saved successfully')
toast.error('Something went wrong', { title: 'Error' })
toast.warning('Session expiring soon', { duration: 8000 })
toast.info('New version available')
toast.brand('Welcome back!')Each method accepts (message, options?) where options are { title?: string, duration?: number }. Default duration is 5000ms.
Theming
Every color in the library flows through CSS custom properties prefixed with --ds-. The default theme is included in style.css.
Option 1: CSS overrides
Override only the variables you need:
/* my-theme.css */
:root {
--ds-brand: #8B5CF6;
--ds-brand-hover: #7C3AED;
--ds-brand-active: #6D28D9;
--ds-brand-on-solid: #FFFFFF;
--ds-bg: #FAFAFA;
/* Only override what differs — everything else inherits defaults */
}import '@leodamours/ds-components/style.css' // defaults
import './my-theme.css' // your overridesOption 2: Generate a full theme from 7 base colors
The generateFullTheme function derives all hover, soft, border, contrast, and disabled variants automatically from just one hex per semantic role:
import { generateFullTheme } from '@leodamours/ds-components'
const css = generateFullTheme(
{
brand: '#8B5CF6',
error: '#E11D48',
success: '#059669',
warning: '#EA580C',
info: '#6366F1',
processing: '#D946EF',
neutral: '#6B5F7D',
},
'[data-theme="purple"]', // CSS selector (defaults to ':root')
)
// Inject into a <style> tag
const style = document.createElement('style')
style.textContent = css
document.head.appendChild(style)For finer control, generatePalette(hex) returns the full variant set for a single color:
import { generatePalette } from '@leodamours/ds-components'
const brandPalette = generatePalette('#8B5CF6')
// => { base, hover, active, disabled, soft, soft-hover, soft-active,
// on-solid, on-soft, border, border-hover, border-strong }Option 3: Override individual tokens at runtime
import { generateThemeCss } from '@leodamours/ds-components'
const css = generateThemeCss(
{ brand: '#8B5CF6', 'brand-hover': '#7C3AED' },
'[data-theme="purple"]',
)Runtime theme switching
A dark theme is bundled and activates via data-theme:
// Switch to dark
document.documentElement.dataset.theme = 'dark'
// Switch back to light
delete document.documentElement.dataset.themeToken naming convention
Tokens follow a role + tone pattern: --ds-{role}[-{tone}][-{state}]
Core UI tokens:
| Token | Purpose |
|---|---|
| bg, bg-secondary, bg-tertiary | Page backgrounds |
| bg-inverse, bg-inverse-secondary | Inverted backgrounds |
| fg, fg-secondary, fg-tertiary, fg-inverse, fg-disabled | Text colors |
| icon, icon-hover, icon-active, icon-inverse, icon-disabled | Icon colors |
| border, border-light, border-strong, border-focus | Border colors |
| surface-hover, surface-hover-light, surface-active, surface-disabled | Interactive surfaces |
| overlay, focus-ring | Overlays and focus states |
Semantic role tokens — each role (brand, error, success, warning, info, processing, neutral) has:
| Tone | Purpose | Example |
|---|---|---|
| {role} | Solid color | --ds-brand |
| {role}-hover | Hover state | --ds-brand-hover |
| {role}-active | Active/pressed state | --ds-brand-active |
| {role}-soft | Light background | --ds-error-soft |
| {role}-soft-hover | Soft hover state | --ds-error-soft-hover |
| {role}-soft-active | Soft active state | --ds-error-soft-active |
| {role}-on-solid | Text on solid bg | --ds-success-on-solid |
| {role}-on-soft | Text on soft bg | --ds-success-on-soft |
| {role}-border | Border color | --ds-warning-border |
| {role}-border-hover | Border hover | --ds-warning-border-hover |
| {role}-border-strong | Strong border | --ds-info-border-strong |
UnoCSS Preset
If your app uses UnoCSS, import the design system preset instead of configuring tokens manually. It includes the full theme, rules, shortcuts, variants, and CSS custom properties.
Basic setup
// uno.config.ts
import { defineConfig, presetUno } from 'unocss'
import { dsPreset } from '@leodamours/ds-components/uno'
export default defineConfig({
presets: [presetUno(), dsPreset()],
})Custom palette
Pass base colors to override the default palette. All hover, soft, border, and contrast variants are derived automatically:
dsPreset({
brand: '#8B5CF6',
error: '#E11D48',
success: '#059669',
warning: '#EA580C',
info: '#6366F1',
processing: '#D946EF',
neutral: '#6B5F7D',
})Only override what differs — unspecified roles keep their defaults. The preset accepts these roles: brand, error, success, warning, info, processing, neutral.
Full export reference
Components
import {
DsAvatar,
DsBadge,
DsButton,
DsCard,
DsCheckbox,
DsDropdownSelect,
DsExpandableCard,
DsInput,
DsLoadingSpinner,
DsModal,
DsRadio,
DsSelect,
DsSlider,
DsSwitch,
DsTable,
DsTabs,
DsTextarea,
DsToast,
DsToastContainer,
DsTooltip,
DsUnderConstruction,
} from '@leodamours/ds-components'Prop types
import type {
DsAvatarProps,
DsBadgeProps,
DsButtonProps,
DsCardProps,
DsCheckboxProps,
DsDropdownSelectProps,
DsExpandableCardProps,
DsInputProps,
DsLoadingSpinnerProps,
DsModalProps,
DsRadioProps,
DsSelectProps,
DsSelectOption,
DsSliderProps,
DsSwitchProps,
DsTableProps,
DsTableColumn,
DsTablePagination,
DsTabsProps,
DsTab,
DsTextareaProps,
DsTooltipProps,
DsUnderConstructionProps,
Toast,
ToastVariant,
} from '@leodamours/ds-components'Token utilities
import {
colorTokens, // All color token values as a JS object
spacingTokens, // Spacing scale values
radiusTokens, // Border radius values
shadowTokens, // Box shadow values
allTokens, // All tokens combined
generateThemeCss, // Override specific tokens → CSS string
tokenToCssVar, // Convert token name to CSS var reference
} from '@leodamours/ds-components'
import type { ColorToken, SpacingToken, RadiusToken, ShadowToken, ThemeOverride } from '@leodamours/ds-components'Palette generation
import {
generatePalette, // Single hex → full SemanticPalette (12 variants)
generateFullTheme, // 7 base colors → complete CSS theme string
expandBases, // 7 base colors → flat token map (no CSS wrapping)
hexToHSL, // Hex → { h, s, l }
hslToHex, // (h, s, l) → hex string
} from '@leodamours/ds-components'
import type { SemanticPalette, ThemeBases, SemanticRole } from '@leodamours/ds-components'Composables
import { useToast, useId } from '@leodamours/ds-components'useToast()— returns{ toasts, removeToast, success, error, warning, info, brand }(see Toast system)useId()— generates unique IDs for accessible form element associations
