vue-smart-ui
Version:
A collection of Vue 3 smart and highly customizable UI components for modern web applications
1,128 lines (903 loc) • 40.5 kB
Markdown
# Vue Components Library
A library of reusable and highly customizable Vue components.
## 📱 Examples
Visit our website to see examples for all components:
[https://vuesmartui.edgarimai.com/](https://vuesmartui.edgarimai.com/)
Here you will find:
- Interactive demonstrations of each component
- Available customization options
## 🚀 How to Use
1. Install the package
```bash
npm install vue-smart-ui
# or
yarn add vue-smart-ui
# or
pnpm add vue-smart-ui
```
2. Import the styles on main.js
```javascript
import 'vue-smart-ui/style.css'
```
3. Import the required components
```javascript
import { BaseButton, BaseInput /* ... */ } from 'vue-smart-ui'
```
4. Use the components in your templates
```vue
<template>
<BaseButton variant="primary"> My Button </BaseButton>
</template>
```
## ⚙️ General Features
- 🎨 Consistent and modern design
- 📱 Fully responsive
- ♿ Accessible
- 🎯 Slot support for maximum flexibility
- 🌈 Theming system via CSS variables
- 🔧 Highly customizable
- ⚡ Optimized performance
## 📦 Available Components
### BaseButton
A versatile and customizable button that supports different variants, sizes, and states.
#### Props
| Prop | Type | Default | Description |
| -------- | ------- | --------- | ------------------------------------------------------------------------- |
| id | String | - | Optional button ID |
| type | String | 'button' | Button style. Options: 'button', 'submit', 'reset' |
| variant | String | 'primary' | Button style. Options: 'primary', 'secondary', 'gray', 'outline', 'ghost' |
| size | String | 'medium' | Button size. Options: 'small', 'medium', 'large', 'auto' |
| block | Boolean | false | If true, button will take 100% of available width |
| disabled | Boolean | false | Disables the button |
| loading | Boolean | false | Shows a loading indicator |
| iconOnly | Boolean | false | If true, button will be square with no padding |
#### Slots
- `default`: Main button content
- `prefix`: Content before main text (e.g., icon)
- `suffix`: Content after main text (e.g., icon)
#### Events
- `click`: Emitted when button is clicked
#### Usage Example
```vue
<BaseButton variant="primary" size="medium" :loading="false" @click="handleClick">
Click Here
</BaseButton>
```
### BaseAccordion and BaseAccordionItem
Accordion system for displaying expandable content.
#### BaseAccordion props
| Prop | Type | Default | Description |
| -------- | ------- | --------- | --------------------------------------------------------------- |
| id | String | - | Optional accordion ID |
| variant | String | 'default' | Accordion style. Options: 'default', 'bordered', 'minimal' |
| multiple | Boolean | false | If true, allows multiple sections to be expanded simultaneously |
#### BaseAccordionItem props
- `title` (String): Required. The title text for the accordion item
- `disabled` (Boolean): If true, disables the accordion item
- Default: false
#### Slots
- `default`: Main content of the accordion item
- `title`: Custom title content (overrides title prop)
- `icon`: Icon content before the title
- `chevron`: Custom chevron/arrow icon
#### Usage Example
```vue
<div class="accordion-container">
<h2>Accordion</h2>
<BaseAccordion multiple variant="bordered">
<BaseAccordionItem title="Section 1">
<p>Section 1 content</p>
</BaseAccordionItem>
<BaseAccordionItem title="Section 2" disabled> Section 2 content </BaseAccordionItem>
<BaseAccordionItem title="Section 3">
<template #title>
<span class="custom-title">Custom title</span>
</template>
Section 3 content
</BaseAccordionItem>
</BaseAccordion>
</div>
```
### BaseInput
Highly customizable input field supporting different input types, input masks (phone, currency, etc), and built-in validation rules.
The BaseInput component provides a rich set of features for form inputs:
- Works with all HTML input types (text, password, email, number, etc)
- **Built-in Validation**:
- Required fields
- Email format
- Min/max length
- Pattern matching
- Custom validation functions
- Customizable error messages
- Form-level validation support
- **Input Masks**:
- Phone numbers: Automatically formats as (00) 00000-0000
- Currency: Formats as currency with proper decimals (e.g. R$ 1.234,56)
- Custom masks support
- **Styling**:
- Label customization
- Error state styling
- Disabled state
- Read-only state
- Focus state
- Placeholder support
#### BaseInput props
| Prop | Type | Default | Description |
| ----------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| modelValue | any | - | The v-model value |
| label | String | - | Input label text |
| type | String | 'text' | HTML input type |
| placeholder | String | - | Input placeholder text |
| disabled | Boolean | false | Disables the input |
| readonly | Boolean | false | Makes input readonly |
| rules | Array | - | Validation rules array. Each rule can be:<br>• String: 'required', 'email', 'min', 'max', 'pattern'<br>• Object: { required: true, message: 'Custom message' }<br>• Object: { min: number, message: 'Custom message' }<br>• Object: { max: number, message: 'Custom message' }<br>• Object: { pattern: RegExp, message: 'Custom message' } |
#### Events
- `@update:modelValue`: Emitted when input value changes
- `@mounted`: Emitted when component is mounted, passes input ref
- `@focus`: Emitted on input focus
- `@blur`: Emitted on input blur
#### Basic example
```vue
<BaseInput v-model="name" label="Name" :rules="['required']" />
```
#### Usage Example with multiple inputs and validation
```js
const formRefs = ref({})
// Function to validate all inputs and show erros if has any
const validateForm = () => {
let isValid = true
// Validate all registered inputs
Object.values(formRefs.value).forEach((input) => {
if (input?.validate && !input.validate()) {
isValid = false
}
})
return isValid
}
const handleSubmit = () => {
if (validateForm()) {
console.log('Form submitted:', pageState.input)
} else {
console.log('Form has errors')
}
}
const registerInput = (name, ref) => {
console.log(name, ref)
if (ref) {
formRefs.value[name] = ref
}
}
```
```vue
<!-- Email validation -->
<BaseInput
v-model="input.email"
label="Email"
:rules="['required', 'email']"
@mounted="(ref) => registerInput('email', ref)"
/>
<!-- Multiple rules with custom messages -->
<BaseInput
v-model="password"
type="password"
label="Password"
:rules="[
{ required: true, message: 'Password is required' },
{ min: 8, message: 'Password must be at least 8 characters' },
]"
@mounted="(ref) => registerInput('password', ref)"
/>
<!-- Custom validator -->
<BaseInput
v-model="username"
label="Username"
:rules="[
{
validator: (value) => /^[a-z0-9]+$/.test(value),
message: 'Username can only contain lowercase letters and numbers',
},
]"
/>
<!-- Phone mask -->
<BaseInput
v-model="phone"
label="Phone"
:rules="['required', 'pattern: ^\\d{10}$']"
@mounted="(ref) => registerInput('phone', ref)"
mask="phone"
placeholder="(00) 00000-0000"
/>
<!-- Currency mask -->
<BaseInput v-model="price" label="Price" mask="currency" placeholder="R$ 0,00" />
```
### BaseTextarea
Highly customizable textarea component that matches the BaseInput styling and functionality. Perfect for multi-line text input with support for validation, different variants, and auto-resize capability.
Features:
- Consistent styling with BaseInput
- Auto-resize capability
- Built-in validation
- Different variants (default, filled, outlined)
- Support for helper text and error states
- Customizable through CSS variables
- Flexible width control (block/inline)
#### Props
| Prop | Type | Default | Description |
| --------------- | --------- | ------------ | ------------------------------------------------------------------ |
| modelValue | `String` | `''` | The v-model value |
| variant | `String` | `'default'` | Textarea style. Options: 'default', 'filled', 'outlined' |
| label | `String` | `null` | Label text |
| placeholder | `String` | `''` | Placeholder text |
| rows | `Number` | `3` | Initial number of rows |
| maxRows | `Number` | `null` | Maximum number of rows when using autoResize |
| disabled | `Boolean` | `false` | Disables the textarea |
| readonly | `Boolean` | `false` | Makes textarea readonly |
| required | `Boolean` | `false` | Makes the field required |
| helperText | `String` | `null` | Helper text displayed below the textarea |
| errorMessage | `String` | `null` | Error message to display |
| rules | `Array` | `[]` | Validation rules array |
| validateOnBlur | `Boolean` | `true` | Trigger validation on blur |
| validateOnInput | `Boolean` | `false` | Trigger validation on input |
| block | `Boolean` | `false` | Makes the textarea full width |
| resize | `String` | `'vertical'` | Resize behavior. Options: 'none', 'both', 'horizontal', 'vertical' |
| autoResize | `Boolean` | `false` | Automatically adjust height based on content |
#### Events
- `@update:modelValue`: Emitted when textarea value changes
- `@focus`: Emitted on textarea focus
- `@blur`: Emitted on textarea blur
- `@input`: Emitted on input
- `@validation`: Emitted when validation occurs, includes validation status
- `@mounted`: Emitted when component is mounted, passes textarea ref
#### Basic Examples
```vue
<!-- Simple textarea -->
<BaseTextarea v-model="description" label="Description" placeholder="Enter your description" />
<!-- With validation -->
<BaseTextarea
v-model="comment"
label="Comment"
:rules="['required', { min: 10, message: 'Comment must be at least 10 characters' }]"
helper-text="Please provide detailed feedback"
/>
<!-- Auto-resizing textarea -->
<BaseTextarea v-model="content" label="Content" :rows="3" :max-rows="10" auto-resize block />
<!-- Filled variant -->
<BaseTextarea v-model="notes" label="Notes" variant="filled" placeholder="Add your notes here" />
<!-- With custom resize behavior -->
<BaseTextarea v-model="text" label="Resizable" resize="both" :rows="4" />
```
#### Form Validation Example
```vue
<template>
<form @submit.prevent="handleSubmit">
<BaseTextarea
v-model="feedback"
label="Feedback"
:rules="[
'required',
{ min: 20, message: 'Feedback must be at least 20 characters long' },
{ max: 500, message: 'Feedback cannot exceed 500 characters' },
]"
block
@mounted="(ref) => registerTextarea('feedback', ref)"
/>
<BaseButton type="submit">Submit Feedback</BaseButton>
</form>
</template>
<script setup>
const formRefs = ref({})
const registerTextarea = (name, ref) => {
if (ref) {
formRefs.value[name] = ref
}
}
const validateForm = () => {
let isValid = true
Object.values(formRefs.value).forEach((textarea) => {
if (textarea?.validate && !textarea.validate()) {
isValid = false
}
})
return isValid
}
const handleSubmit = () => {
if (validateForm()) {
console.log('Form is valid, submitting...')
}
}
</script>
```
### BaseCheckbox
Checkbox component with customizable size, state and styling. Supports single checkbox or group functionality.
Features:
- Three sizes (small, medium, large)
- Disabled state
- Indeterminate state
- Custom colors and styling
- Group functionality for multiple checkboxes
#### Props
| Prop | Type | Default | Description |
| ------------- | -------------------------------- | ----------- | --------------------------------- |
| modelValue | `boolean \| Array` | `false` | v-model binding value |
| label | `string` | `''` | Label text for the checkbox |
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | Size of the checkbox |
| disabled | `boolean` | `false` | Whether checkbox is disabled |
| indeterminate | `boolean` | `false` | Show indeterminate state |
| value | `any` | `undefined` | Value when used in checkbox group |
| name | `string` | `''` | Input name attribute |
#### Basic examples
```vue
<BaseCheckbox v-model="checked" label="Basic checkbox" />
<BaseCheckbox v-model="checked">
Checkbox with <span style="color: purple">slots</span>
</BaseCheckbox>
<BaseCheckbox v-model="checked" error label="Required field with error" />
<BaseCheckbox v-model="checked" disabled label="Disabled checkbox" />
<BaseCheckbox v-model="checked" size="small" label="Small option" />
<BaseCheckbox v-model="checked" size="medium" label="Medium option" />
<BaseCheckbox v-model="checked" size="large" label="Large option" />
```
#### Example with multiple options
```js
const pageState = reactive({
checkbox: {
checked: false,
selectedFruits: [],
selectAll: false,
someSelected: false,
},
fruits: [
{
name: 'Apple',
value: 'apple',
},
{
name: 'Banana',
value: 'banana',
},
{
name: 'Orange',
value: 'orange',
},
{
name: 'Pineapple',
value: 'pineapple',
},
],
})
const allSelected = computed(() => {
return pageState.checkbox.selectedFruits.length === pageState.fruits.length
})
const someSelected = computed(() => {
return pageState.checkbox.selectedFruits.length > 0 && !allSelected.value
})
const handleSelectAll = (checked) => {
if (!checked) return (pageState.checkbox.selectedFruits = [])
pageState.fruits.forEach((fruit) => {
if (!pageState.checkbox.selectedFruits.includes(fruit.value)) {
pageState.checkbox.selectedFruits.push(fruit.value)
}
})
}
```
```vue
<BaseCheckbox
v-for="fruit in pageState.fruits"
:key="fruit.value"
v-model="pageState.checkbox.selectedFruits"
:value="fruit.value"
:label="fruit.name"
/>
<BaseCheckbox
v-model="allSelected"
:indeterminate="someSelected"
label="Select all"
@update:model-value="handleSelectAll"
/>
```
### BaseToast
Toast notification system for user feedback. Provides a flexible way to show notifications with different variants (success, error, warning, info), positions, and durations.
Features:
- Multiple toast positions (top-right, top-center, top-left, bottom-right, bottom-center, bottom-left)
- Customizable duration
- Different variants with appropriate styling
- Pause timer on hover
- Auto-dismiss with configurable timeout
- Stacking multiple toasts
- Smooth enter/exit animations
#### Base Toast props
| Prop | Type | Default | Description |
| ------------ | ------------------------------------------------------------------------------------------------- | ------------- | --------------------------------------------------------- |
| variant | `'default' \| 'success' \| 'error' \| 'warning' \| 'info'` | `'default'` | Style variant of the toast |
| title | `string` | `undefined` | Optional title text shown at top of toast |
| message | `string` | `''` | Main toast message content |
| duration | `number` | `5000` | Time in ms before auto-dismissing (0 for no auto-dismiss) |
| position | `'top-right' \| 'top-center' \| 'top-left' \| 'bottom-right' \| 'bottom-center' \| 'bottom-left'` | `'top-right'` | Position where toast appears |
| closable | `boolean` | `true` | Whether to show close button |
| pauseOnHover | `boolean` | `true` | Pause dismiss timer when hovering |
| simple | `boolean` | `false` | Simple toast option |
#### Basic example
```vue
<script setup>
import { useToast } from 'vue-smart-ui'
const toast = useToast()
const showToast = () => {
toast.success('Basic toast')
}
const showCustom = () => {
toast.warning('Custom toast', {
title: 'Title',
position: 'bottom-center',
duration: 5000,
})
}
const showSimple = () => {
toast.error('Simple toast', {
position: 'bottom-center',
duration: 5000,
simple: true,
})
}
</script>
```
### BaseInfiniteScroll
Component for implementing infinite scroll functionality.
#### Props
| Prop | Type | Default | Description |
| ----------- | --------- | ------------------------- | ---------------------------------------------------------- |
| loading | `boolean` | `false` | Whether more content is currently being loaded |
| disabled | `boolean` | `false` | Disable infinite scroll functionality |
| threshold | `number` | `20` | Distance in pixels from bottom before triggering load more |
| container | `string` | `null` | CSS selector for scroll container. If null, uses window |
| loadingText | `string` | `'Loading more items...'` | Text shown during loading. If empty, no text is shown |
| endText | `string` | `'No more items to load'` | Text shown when disabled. If empty, no text is shown |
#### Slots
- `default`: Main content area for the list of items
- `loading`: Custom loading indicator when fetching more items
- `disabled`: Custom message or content when there are no more items to load
#### Basic example
```vue
<BaseInfiniteScroll :loading="loading" :disabled="!hasMore" @load-more="loadMore">
<!-- Items list -->
<div class="items-grid">
<div v-for="item in pageState.infiniteScroll.items" :key="item.id" class="item">
{{ item.title }}
</div>
</div>
<!-- Loading custom -->
<template #loading>
<div class="custom-loader">Loading more items...</div>
</template>
<!-- End message -->
<template #disabled>
<div class="end-message">There are no more items to load</div>
</template>
</BaseInfiniteScroll>
```
### BaseSlider
A highly customizable slider component that supports single values or ranges, with custom marks, value formatting, and different visual states.
Features:
- Support for single values or ranges
- Customizable marks for specific points
- Custom value formatting
- Different visual states (success, error, warning)
- Configurable minimum and maximum values
- Adjustable steps
- Fully accessible with keyboard support
- Display of values at extremities and above controls
#### Props
| Prop | Type | Default | Description |
| ------------ | ----------------- | ----------- | ------------------------------------------------ |
| modelValue | `Number \| Array` | `0` | Current slider value (single or array for range) |
| min | `Number` | `0` | Minimum value of the slider |
| max | `Number` | `100` | Maximum value of the slider |
| step | `Number` | `1` | Increment between values |
| label | `String` | `null` | Label text for the slider |
| disabled | `Boolean` | `false` | Disables the slider |
| required | `Boolean` | `false` | Marks the field as required |
| helperText | `String` | `null` | Helper text displayed below the slider |
| errorMessage | `String` | `null` | Error message to display |
| state | `String` | `null` | Visual state: 'success', 'error', 'warning' |
| showValue | `Boolean` | `true` | Displays the current value above the control |
| range | `Boolean` | `false` | Enables range mode with two controls |
| variant | `String` | `'default'` | Visual variant: 'default', 'filled' |
| marks | `Array` | `[]` | Marks for specific values |
| formatValue | `Function` | `null` | Function to format the display of values |
#### Events
- `@update:modelValue`: Emitted when the slider value changes
- `@change`: Emitted when the user changes the value
- `@mounted`: Emitted when the component is mounted, passes the slider reference
#### Basic Examples
```vue
<!-- Simple slider -->
<BaseSlider v-model="volume" label="Volume" />
<!-- Slider with value formatting -->
<BaseSlider
v-model="price"
:min="0"
:max="1000"
:format-value="(val) => `$${val.toFixed(2)}`"
label="Price"
/>
<!-- Slider with marks -->
<BaseSlider
v-model="rating"
:min="0"
:max="5"
:step="0.5"
:marks="[
{ value: 0, label: 'Poor' },
{ value: 2.5, label: 'Average' },
{ value: 5, label: 'Excellent' },
]"
label="Rating"
/>
<!-- Range slider -->
<BaseSlider
v-model="priceRange"
:min="0"
:max="1000"
range
:format-value="(val) => `$${val}`"
label="Price Range"
/>
<!-- Slider with error state -->
<BaseSlider
v-model="value"
state="error"
error-message="Please select a higher value"
label="Value"
/>
```
#### Validation Example
```vue
<template>
<form @submit.prevent="handleSubmit">
<BaseSlider
v-model="formData.budget"
label="Budget"
:min="100"
:max="10000"
:step="100"
:format-value="(val) => `$${val.toLocaleString()}`"
:marks="[
{ value: 1000, label: 'Basic' },
{ value: 5000, label: 'Intermediate' },
{ value: 10000, label: 'Premium' },
]"
@mounted="(ref) => registerSlider('budget', ref)"
/>
<BaseButton type="submit">Submit</BaseButton>
</form>
</template>
<script setup>
const formRefs = ref({})
const registerSlider = (name, ref) => {
if (ref) {
formRefs.value[name] = ref
}
}
const validateForm = () => {
let isValid = true
// Validate if budget is at least 1000
const budgetRef = formRefs.value.budget
if (budgetRef && budgetRef.getValue() < 1000) {
budgetRef.setError('Minimum budget is $1,000')
isValid = false
}
return isValid
}
const handleSubmit = () => {
if (validateForm()) {
console.log('Form is valid, submitting...')
}
}
</script>
```
### BaseDropdown
Customizable dropdown menu component that provides a toggleable menu with positioning and click handling.
#### Props
| Prop | Type | Default | Description |
| ------------------- | --------- | --------- | ----------------------------------------------- |
| modelValue | `boolean` | `false` | Controls visibility of dropdown (v-model) |
| variant | `string` | `default` | Visual style - 'default', 'white', or 'dark' |
| width | `string` | `auto` | Width of dropdown menu |
| closeOnClick | `boolean` | `true` | Whether to close dropdown when item clicked |
| closeOnClickOutside | `boolean` | `true` | Whether to close dropdown when clicking outside |
#### Slots
- `trigger`: Content that triggers the dropdown (usually a button)
- `default`: Content of the dropdown menu
#### Basic example
```vue
<BaseDropdown v-model="show" variant="default">
<!-- Trigger -->
<template #trigger>
<BaseButton>Open Dropdown</BaseButton>
</template>
<!-- Dropdown items -->
<div class="base-dropdown-item">Profile</div>
<div class="base-dropdown-item">Settings</div>
<div class="base-dropdown-item" data-prevent-close>Notifications</div>
<div class="base-dropdown-item" style="color: red">Logout</div>
</BaseDropdown>
```
### BaseSkeleton
Component for displaying loading states and placeholders while content is being loaded. Supports different variants like text, headings, circles, and rectangles with optional animations.
#### Props
| Prop | Type | Default | Description |
| -------- | --------- | ----------- | --------------------------------------------------------------------- |
| variant | `string` | `rectangle` | Type of skeleton - 'rectangle', 'circle', 'text', 'heading', 'button' |
| width | `string` | `100%` | Width of the skeleton element |
| height | `string` | - | Height of the skeleton element (overrides variant default) |
| rounded | `boolean` | `false` | Whether to apply rounded corners |
| animated | `boolean` | `true` | Whether to show loading animation |
#### Basic examples
```vue
<!-- Avatar -->
<BaseSkeleton variant="circle" width="5rem" height="5rem" />
<!-- Title -->
<BaseSkeleton variant="heading" width="60%" />
<!-- Text -->
<BaseSkeleton variant="text" />
<BaseSkeleton variant="text" />
<BaseSkeleton variant="text" />
<!-- Button -->
<BaseSkeleton variant="button" rounded />
```
### BasePopup
Customizable modal/popup component with support for different variants, animations and positioning. Provides a flexible way to display modal dialogs, alerts and confirmation boxes.
#### Props
| Prop | Type | Default | Description |
| ------------------- | --------- | --------- | ---------------------------------------------------------------------- |
| modelValue | `boolean` | `false` | Controls visibility of the popup (v-model) |
| variant | `string` | `default` | Popup style variant - 'default', 'info', 'success', 'warning', 'error' |
| size | `string` | `medium` | Size of the popup - 'small', 'medium', 'large' |
| position | `string` | `center` | Position of the popup - 'center', 'top', 'bottom', 'left', 'right' |
| disableClickOutside | `boolean` | `false` | Whether clicking outside closes the popup |
| closeOnEsc | `boolean` | `true` | Whether pressing Escape key closes the popup |
#### Slots
- `default`: Main content of the popup
- `trigger`: Element that triggers the popup when clicked
#### Usage example
```vue
<BasePopup v-model="show" :variant="variant" :size="size" :position="position">
<template #header>
<p style="font-size: 1.2rem; font-weight: bold; text-align: center; margin-block: 0.5rem">
Configured Popup
</p>
</template>
<div class="popup-content">
<p>Current configuration:</p>
<ul>
<li>Variant: {{ variant }}</li>
<li>Size: {{ size }}</li>
<li>Position: {{ position }}</li>
</ul>
</div>
<template #footer>
<div style="text-align: center">
<BaseButton @click="show = false">Close</BaseButton>
</div>
</template>
<template #close>
<span>×</span>
</template>
</BasePopup>
```
## 🎨 Customization
### CSS Variables
The cpomponents can be customized in multiple ways:
- Using CSS variables for theming
- Overriding component classes directly
- Using Tailwind CSS classes
#### Available CSS variables for each component
```css
:root {
// Base colors
--color-primary: #3b82f6;
--color-primary-dark: #2563eb;
--color-primary-light: #e0e7ff;
--color-primary-text: #1e40af;
--color-secondary: #9333ea;
--color-secondary-dark: #7e22ce;
--color-secondary-light: #ede9fe;
--color-secondary-text: #4338ca;
--color-info: #3b82f6;
--color-info-dark: #0284c7;
--color-info-light: #dbeafe;
--color-success: #22c55e;
--color-success-dark: #15803d;
--color-success-light: #f0fdf4;
--color-warning: #f59e0b;
--color-warning-dark: #92400e;
--color-warning-light: #fefce8;
--color-error: #ef4444;
--color-error-dark: #991b1b;
--color-error-light: #fee2e2;
// Background colors
--bg-default: #ffffff;
--bg-subtle: #f9fafb;
--bg-muted: #f3f4f6;
--bg-emphasized: #e5e7eb;
--bg-disabled: #f3f4f6;
// Surface colors (for cards, modals, etc.)
--surface-default: #f9fafb;
--surface-hover: #f3f4f6;
--surface-active: #e5e7eb;
--surface-selected: #eff6ff;
// Text colors
--text-default: #111827;
--text-muted: #374151;
--text-subtle: #6b7280;
--text-disabled: #9ca3af;
--text-inverse: #ffffff;
// Border colors
--border-default: #e5e7eb;
--border-strong: #c0c4c9;
--border-focus: #d1d5db;
--color-white: #ffffff;
--color-black: #000000;
// Font base
--font-family-base: 'Roboto', sans-serif;
// --font-size-base: 62.5%;
// Buttons variables
--button-primary-bg: var(--color-primary);
--button-primary-hover: var(--color-primary-dark);
--button-primary-text: var(--color-white);
--button-secondary-bg: var(--color-secondary);
--button-secondary-hover: var(--color-secondary-dark);
--button-secondary-text: var(--color-white);
--button-gray-bg: var(--bg-muted);
--button-gray-hover: var(--bg-emphasized);
--button-gray-text: var(--text-muted);
--button-outline-border: var(--border-strong);
--button-outline-hover: var(--bg-muted);
--button-outline-text: var(--text-muted);
--button-ghost-text: var(--text-muted);
--button-focus-outline: var(--color-primary);
--button-small-font-size: 0.875rem;
--button-medium-font-size: 1rem;
--button-large-font-size: 1.125rem;
// Popups variables
--popup-default-color: var(--text-subtle);
--popup-info-color: var(--color-info);
--popup-success-color: var(--color-success);
--popup-warning-color: var(--color-warning);
--popup-error-color: var(--color-error);
--popup-border-color: var(--border-strong);
--popup-default-bg: var(--surface-default);
--popup-info-bg: var(--surface-default);
--popup-success-bg: var(--surface-default);
--popup-warning-bg: var(--surface-default);
--popup-error-bg: var(--surface-default);
// Toasts variables
--toast-default-bg: var(--surface-default);
--toast-default-color: var(--border-strong);
--toast-default-text: var(--text-default);
--toast-info-bg: var(--color-primary-light);
--toast-info-color: var(--color-primary);
--toast-info-text: var(--color-primary-text);
--toast-success-bg: var(--color-success-light);
--toast-success-color: var(--color-success);
--toast-success-text: var(--color-success-dark);
--toast-warning-bg: var(--color-warning-light);
--toast-warning-color: var(--color-warning);
--toast-warning-text: var(--color-warning-dark);
--toast-error-bg: var(--color-error-light);
--toast-error-color: var(--color-error);
--toast-error-text: var(--color-error-dark);
--toast-title-font-size: 1rem;
--toast-message-font-size: 0.75rem;
// Dropdowns variables
--dropdown-bg: var(--surface-default);
--dropdown-border-color: var(--border-default);
--dropdown-item-hover-bg: var(--surface-hover);
--dropdown-dark-bg: var(--text-muted);
--dropdown-dark-border-color: #4b5563;
--dropdown-dark-item-hover-bg: #4b5563;
// Skeletons variables
--skeleton-bg: var(--bg-emphasized);
--skeleton-rectangle-height: 1.5rem;
--skeleton-circle-size: 3rem;
--skeleton-heading-height: 2rem;
--skeleton-button-height: 2.5rem;
--skeleton-button-width: 8rem;
--skeleton-text-height: 1rem;
--skeleton-rounded-radius: 0.375rem;
--skeleton-shine: rgba(255, 255, 255, 0.3);
// Accordions variables
--accordion-border-color: var(--border-default);
--accordion-header-color: var(--text-default);
--accordion-hover-bg: var(--bg-subtle);
--accordion-focus-ring: var(--color-primary);
// Input variables
--input-label-color: var(--text-muted);
--input-required-color: var(--color-error);
--input-helper-color: var(--text-subtle);
--input-border-color: var(--border-strong);
--input-bg: var(--color-white);
--input-focus-border-color: var(--color-primary);
--input-focus-ring-color: rgba(59, 130, 246, 0.1);
--input-placeholder-color: var(--text-disabled);
--input-disabled-bg: var(--bg-muted);
--input-disabled-text: var(--text-subtle);
--input-icon-color: var(--text-subtle);
--input-filled-bg: var(--bg-muted);
--input-filled-focus-bg: var(--bg-subtle);
--input-success-color: var(--color-success);
--input-error-color: var(--color-error);
--input-warning-color: var(--color-warning);
--input-label-font-size: 0.875rem;
--input-field-font-size: 0.875rem;
--input-helper-font-size: 0.75rem;
// Checkbox variables
--checkbox-border: 1px solid var(--checkbox-border-color);
--checkbox-border-color: var(--border-strong);
--checkbox-hover-border-color: var(--bg-emphasized);
--checkbox-border-radius: 0.25rem;
--checkbox-bg: var(--color-white);
--checkbox-check: var(--text-muted);
--checkbox-text: var(--text-muted);
--checkbox-small-font-size: 0.875rem;
--checkbox-medium-font-size: 1rem;
--checkbox-large-font-size: 1.125rem;
// Textarea variables
--textarea-font-size: 1rem;
--textarea-font-family: var(--font-family-base);
--textarea-line-height: 1.6;
--textarea-padding: 0.75rem 1rem;
--textarea-border-radius: 0.5rem;
--textarea-bg: var(--color-white);
--textarea-text-color: var(--text-default);
--textarea-border-color: var(--border-strong);
--textarea-focus-border-color: var(--color-primary);
--textarea-success-color: var(--color-success);
--textarea-error-color: var(--color-error);
--textarea-warning-color: var(--color-warning);
--textarea-filled-bg: var(--bg-muted);
--textarea-filled-hover-bg: var(--bg-emphasized);
--textarea-filled-focus-bg: var(--bg-subtle);
--textarea-helper-font-size: 0.75rem;
--textarea-label-font-size: 0.875rem;
// Slider variables
--slider-track-height: 6px;
--slider-track-bg: var(--surface-active);
--slider-track-radius: 3px;
--slider-fill-color: var(--color-primary);
--slider-thumb-size: 18px;
--slider-thumb-bg: var(--surface-default);
--slider-thumb-border-width: 2px;
--slider-thumb-border-color: var(--color-primary);
--slider-thumb-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
--slider-focus-ring-color: rgba(59, 130, 246, 0.1);
--slider-value-font-size: 0.875rem;
--slider-value-color: var(--text-muted);
--slider-value-bubble-bg: var(--surface-default);
--slider-value-bubble-color: var(--text-muted);
--slider-value-bubble-font-size: 0.75rem;
--slider-value-bubble-padding: 2px 6px;
--slider-value-bubble-radius: 4px;
--slider-value-bubble-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
--slider-value-bubble-border: 1px solid var(--color-primary);
--slider-mark-size: 4px;
--slider-mark-color: var(--border-strong);
--slider-mark-label-font-size: 0.75rem;
--slider-mark-label-color: var(--text-subtle);
--slider-mark-label-max-width: 80px;
--slider-label-font-size: 0.875rem;
--slider-label-color: var(--text-muted);
--slider-required-color: var(--color-error);
--slider-helper-font-size: 0.75rem;
--slider-helper-color: var(--text-subtle);
--slider-disabled-opacity: 0.6;
--slider-disabled-track-bg: var(--bg-muted);
--slider-disabled-thumb-bg: var(--bg-muted);
--slider-disabled-thumb-border: var(--text-subtle);
--slider-success-color: var(--color-success);
--slider-success-ring-color: rgba(34, 197, 94, 0.1);
--slider-error-color: var(--color-error);
--slider-error-ring-color: rgba(239, 68, 68, 0.1);
--slider-warning-color: var(--color-warning);
--slider-warning-ring-color: rgba(245, 158, 11, 0.1);
--slider-filled-bg: var(--bg-muted);
}
```