Note
This is a fork of the original Origin UI project. This project is not affiliated with the original. I'm grateful for their work and have created these Svelte components copied from their design.
Note
This is a work in progress. For some components the necessary libraries are coming soon (e.g. Bits UI) or are just not available yet (e.g. React Payment Inputs). Maybe i will add them in the future myself.
Origin UI - Svelte is a collection of copy-and-paste components for quickly building app UIs using Svelte.
- Features
- Demo
- Acknowledgements
- Differences from the original
- Getting Started
- Usage
- Contributing
- Need Help?
- Terms of Use
- Contact
- Notes
- Origin UI - The original project that this Svelte version is copied from
- Svelte
- TailwindCSS v4
- Lucide Icons
- Bits UI
The Original Origin UI is built with Next.js. This is a built with Svelte.
- Svelte instead of
Next.js - Lucide Svelte instead of
Lucide React - Bits UI instead of
Radix UI
If you want to use the components in your project, you need to setup the following:
- Svelte
- TailwindCSS
- Bits UI
- or other dependencies (see src/lib/constants.ts)
Note
This project uses pnpm as package manager.
-
Setup
git clone https://github.com/max-got/originui-svelte.git cd originui-svelte pnpm install
-
Development
pnpm dev
- Components are previewed at
http://localhost:5173
- Components are previewed at
-
Code Quality
pnpm lint # Run ESLint pnpm format # Run Prettier
You can copy and use the components in your Svelte project. Note that some components may require additional libraries - refer to the listed dependencies in the component preview.
In the src/lib/utils.ts
folder you will find the common cn
function for tailwindcss class merging.
In the src/lib/hooks
folder you will find the common hooks.
You need to copy the base components from the src/lib/components/ui
folder to your project and adjust the imports accordingly.
Import the CSS in your src/lib/app.css
file (the following is based on tailwindcss):
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-sans);
--font-mono: var(--font-mono);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive-foreground: var(--destructive-foreground);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
}
:root {
--radius: 0.625rem;
--background: oklch(1 0 0); /* --color-white */
--foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
--card: oklch(1 0 0); /* --color-white */
--card-foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
--popover: oklch(1 0 0); /* --color-white */
--popover-foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
--primary: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
--primary-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
--secondary: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
--secondary-foreground: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
--muted: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
--muted-foreground: oklch(0.552 0.016 285.938); /* --color-zinc-500 */
--accent: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
--accent-foreground: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
--destructive: oklch(0.637 0.237 25.331); /* --color-red-500 */
--destructive-foreground: oklch(0.637 0.237 25.331); /* --color-red-500 */
--border: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
--input: oklch(0.871 0.006 286.286); /* --color-zinc-300 */
--ring: oklch(0.871 0.006 286.286); /* --color-zinc-300 */
}
.dark {
--background: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
--foreground: oklch(0.985 0 0); /* --color-zinc-50 */
--card: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
--card-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
--popover: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
--popover-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
--primary: oklch(0.985 0 0); /* --color-zinc-50 */
--primary-foreground: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
--secondary: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
--secondary-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
--muted: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
--muted-foreground: oklch(0.65 0.01 286); /* π₯ near --color-zinc-400 */
--accent: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
--accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
--destructive: oklch(0.396 0.141 25.723); /* --color-red-900 */
--destructive-foreground: oklch(0.637 0.237 25.331); /* --color-red-500 */
--border: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
--input: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
--ring: oklch(0.442 0.017 285.786); /* --color-zinc-600 */
}
We welcome contributions to Origin UI - Svelte!
This guide will help you understand our project structure and contribution workflow.
src/
βββ lib/
β βββ components/ # UI Components organized by type
β β βββ ui/ # Base UI components
β β β βββ badge.svelte # one component
β β β βββ button.svelte # one component
β β β βββ accordion/ # needs multiple components
β β β β βββ accordion-item.svelte # Base component Accordion Item
β β β β βββ accordion-trigger.svelte # Base component Accordion Trigger
β β β β βββ ...
β β β βββ ...
β β βββ inputs/ # Input components
β β β βββ input-01.svelte
β β β βββ input-02.svelte
β β β βββ ...
β β βββ buttons/ # Button components
β β β βββ button-01.svelte
β β β βββ button-02.svelte
β β β βββ ...
β β ββ ... # Other component categories
β βββ utils/ # Utility functions
β βββ types/ # TypeScript type definitions
β βββ hooks/ # Svelte hooks
The project uses an automated component registry system that connects your components to the routing system. Here's how it works:
-
Registry Generation
pnpm generate:registry
This script automatically:
- Scans the
src/lib/components/
directory - Generates
src/lib/componentRegistry.types.ts
with component mappings - Updates component type definitions and directory structures
- Scans the
-
Registry Architecture
src/ βββ lib/ β βββ componentRegistry.types.ts # Auto-generated types β βββ componentRegistry.components.ts # Auto-generated variables β βββ componentRegistry.ts # Registry implementation β βββ data/api/components/ β βββ components.ts # API endpoints β βββ components.handler.ts # Request handlers β βββ components.route.ts # Route definitions βββ routes/ β βββ (components)/ β βββ [path=componentsPath]/ β βββ +page.server.ts # Dynamic route handling β βββ [directory]/[id]/ β βββ +page.server.ts # Component page handling βββ params/ βββ componentDirectory.ts # Directory parameter validation βββ componentId.ts # Component ID validation
-
How It Works
- The registry system creates a type-safe mapping between:
- Component files in your directories
- URL routes and parameters
- API endpoints
- SvelteKit param matchers use these types to validate URLs
- The system enables dynamic routing while maintaining type safety
- The registry system creates a type-safe mapping between:
-
When to Run Registry Generation Run
pnpm generate:registry
when you:- Add new component files
- Create new component categories
- Rename components or directories
- Update component structure
Important
Always run pnpm generate:registry
after making changes to component files or structure. This ensures the routing system stays in sync with your components.
The project includes an automated dependency detection system that analyzes component source code and manages dependencies.
-
Dependency Configuration
Dependencies must be registered in
src/lib/constants.ts
:export const POSSIBLE_DEPENDENCIES = [ { dev: false, // Whether it's a dev dependency name: 'bits-ui', // Import name to detect packageName: 'bits-ui@next', // NPM package name url: 'https://github.com/huntabyte/bits-ui' // Reference URL } // ... other dependencies ] as const;
Important
When using a new dependency in your component, you MUST add it to POSSIBLE_DEPENDENCIES
first.
This ensures the dependency detection system can find and document it correctly.
-
Adding New Dependencies
To add a new dependency:
- Open
src/lib/constants.ts
- Add an entry to
POSSIBLE_DEPENDENCIES
:{ dev: boolean, // true for devDependencies, false for dependencies name: string, // The import path to detect (e.g., 'your-package') packageName: string, // The exact package name with version (e.g., 'your-package@1.0.0') url: string // Package repository or documentation URL }
- Commit the change before adding components that use this dependency
- Open
-
How It Works
The system (
src/lib/utils/handleComponentSource.ts
):- Scans component source code for imports
- Matches imports against
POSSIBLE_DEPENDENCIES
- Generates installation commands
- Adds dependency information to component metadata
-
Special Cases
-
Enhanced Images:
// Automatically detects and adds @sveltejs/enhanced-img const enhancedImageMatch = source.match(ENHANCED_IMAGE_REGEX);
-
Multiple Dependencies:
# Combines dev and runtime dependencies pnpm i -D @iconify-json/ri unplugin-icons && pnpm i bits-ui
-
-
Integration
The system integrates with:
- Component API endpoints
- Documentation generation
- Development tooling
Tip
When adding new components that use external packages:
- First add the dependency to
POSSIBLE_DEPENDENCIES
- Then create your component with the imports
- The system will automatically detect and document the dependencies
-
1. Component Organization
-
1.1. Base (
/ui
) componentsBase components are in the
src/lib/components/ui
folder.Each component should be in its own directory if it needs multiple components.
component-category/ βββ index.ts # Exports βββ component.svelte # Main component
If a component is a simple one, it can be placed in the
src/lib/components/ui
folder.component-category/ βββ component.svelte # Main component
-
1.2. Origin UI components
Origin UI components are in the
src/lib/components
folder.Each component should be in its own desired category (e.g.
inputs
,buttons
, etc.)component-category/ βββ category-01.svelte βββ category-02.svelte βββ ...
If a component isn't achievable (e.g. not the necessary libraries available, not enough time to implement, etc.) you need to add a placeholder component in the
src/lib/components
folder. In this case the component should be namedcategory-03.todo.svelte
.component-category/ βββ category-03.todo.svelte
-
-
Component Requirements
- Resemble the original component as closely as possible
- If there is a "better" way to implement the component, use that
- Use Svelte 5 runes for state management
- Include TypeScript types for props and events
- Implement proper ARIA attributes for accessibility
-
Styling Guidelines
- Use Tailwind CSS for all styling
- Follow the project's color scheme using CSS variables
- Implement responsive design using Tailwind breakpoints
- Use semantic HTML elements
- Maintain dark mode support
-
Before Starting
- Check existing issues and PRs to avoid duplicate work
- For new features, open an issue for discussion first
- Fork the repository and create a feature branch:
git checkout -b feature/your-feature-name
-
Component Development
- Follow the component organization guidelines
- Place components in appropriate directories
- Use the correct naming convention:
component-category/ βββ category-XX.svelte # Regular component βββ category-XX.todo.svelte # Not yet possible
- Run
pnpm generate:registry
after adding/modifying components
-
Quality Checklist
- Component matches original design
- Svelte 5 runes used correctly
- TypeScript types are complete
- ARIA attributes implemented
- Dark mode works correctly
- Responsive design tested
- No console errors/warnings
- Code formatted (
pnpm format
) - Linting passes (
pnpm lint
)
-
Route Configuration
- For existing categories:
- Ensure component follows naming pattern
- For existing categories:
-
Documentation
- Include example usage
-
Submitting the PR
git add . git commit -m "feat: add new component category" git push origin feature/your-feature-name
- Use conventional commit messages:
feat:
for new components/featuresfix:
for bug fixesdocs:
for documentationrefactor:
for code improvementschore:
for maintenance
- Use conventional commit messages:
Note
Large PRs are harder to review. Consider breaking big changes into smaller, focused PRs.
- Check existing issues and pull requests
- Join our discussions in the repository
- Open an issue for questions
We appreciate your contributions to the Svelte Community and to this project!
Feel free to use these components in personal and commercial projects. However, while the tutorials and demos are available for your use as-is, they cannot be redistributed or resold.
For any questions or feedback, please open an issue on this repository.
This project is a work in progress, and i am continuously working to improve and expand this collection.