feat: populate getting-started area

This commit is contained in:
zernonia 2023-09-04 13:43:47 +08:00
parent 91d6c400ea
commit a0f2f0a37f
35 changed files with 1396 additions and 49 deletions

View File

@ -0,0 +1,26 @@
<script setup lang="ts">
import {
Alert,
AlertDescription,
AlertTitle,
} from '@/lib/registry/default/ui/alert'
interface CalloutProps {
icon?: string
title?: string
}
defineProps<CalloutProps>()
</script>
<template>
<Alert class="not-docs">
<span v-if="icon" class="mr-4 text-2xl">{{ icon }}</span>
<AlertTitle v-if="title">
{{ title }}
</AlertTitle>
<AlertDescription>
<slot />
</AlertDescription>
</Alert>
</template>

View File

@ -17,7 +17,7 @@ const Component = defineAsyncComponent({
</script>
<template>
<div class="group relative my-4 flex flex-col space-y-2">
<div class="not-docs group relative my-4 flex flex-col space-y-2">
<Tabs default-value="preview" class="relative mr-auto w-full">
<div class="flex items-center justify-between pb-3">
<TabsList class="w-full justify-start rounded-none border-b bg-transparent p-0">

View File

@ -0,0 +1,7 @@
<template>
<div
class="[&>h3]:step mb-12 ml-4 border-l pl-8 [counter-reset:step]"
>
<slot />
</div>
</template>

View File

@ -1,2 +1,4 @@
export { default as ComponentPreview } from './ComponentPreview.vue'
export { default as Callout } from './Callout.vue'
export { default as ManualInstall } from './ManualInstall.vue'
export { default as Steps } from './Steps.vue'

View File

@ -24,7 +24,7 @@ export const docsConfig: DocsConfig = {
mainNav: [
{
title: 'Documentation',
href: '/docs',
href: '/docs/introduction',
},
{
title: 'Components',
@ -40,7 +40,7 @@ export const docsConfig: DocsConfig = {
},
{
title: 'GitHub',
href: 'https://github.com/huntabyte/shadcn-svelte',
href: 'https://github.com/radix-vue/shadcn-vue',
external: true,
},
],
@ -50,7 +50,7 @@ export const docsConfig: DocsConfig = {
items: [
{
title: 'Introduction',
href: '/docs',
href: '/docs/introduction',
items: [],
},
{

View File

@ -1,6 +1,7 @@
<script setup lang="ts">
import { useDark, useToggle } from '@vueuse/core'
import { Content, useData, useRoute } from 'vitepress'
import { docsConfig } from '../config/docs'
import { Button } from '@/lib/registry/default/ui/button'
import { Kbd } from '@/lib/registry/default/ui/kbd'
import RadixIconsGithubLogo from '~icons/radix-icons/github-logo'
@ -65,19 +66,19 @@ const links = [
<a href="/" class="text-md font-bold"> shadcn-vue </a>
<nav
v-for="route in routes"
:key="route.name"
v-for="route in docsConfig.mainNav"
:key="route.title"
class="flex items-center space-x-6 text-sm font-medium"
>
<a
:href="route.path"
:target="route.target"
:href="route.href"
:target="route.external ? '_target' : undefined"
class="transition-colors hover:text-foreground/80 text-foreground/60"
:class="{
'font-semibold !text-foreground': $route.path === `${route.path}.html`,
'font-semibold !text-foreground': $route.path === `${route.href}.html`,
}"
>
{{ route.name }}
{{ route.title }}
</a>
</nav>
</div>

View File

@ -3,7 +3,7 @@
--shiki-color-background: #ffffff;
--shiki-token-constant: #ffffff;
--shiki-token-string: #ffffff88;
--shiki-token-comment: #880000;
--shiki-token-comment: #ffffff88;
--shiki-token-keyword: #ffffff88;
--shiki-token-parameter: #AA0000;
--shiki-token-function: #ffffff;

View File

@ -10,24 +10,24 @@
* Headings
* -------------------------------------------------------------------------- */
.vp-doc h1,
.vp-doc h2,
.vp-doc h3,
.vp-doc h4,
.vp-doc h5,
.vp-doc h6 {
.vp-doc h1:not(:where(.not-docs *)),
.vp-doc h2:not(:where(.not-docs *)),
.vp-doc h3:not(:where(.not-docs *)),
.vp-doc h4:not(:where(.not-docs *)),
.vp-doc h5:not(:where(.not-docs *)),
.vp-doc h6:not(:where(.not-docs *)) {
position: relative;
font-weight: 600;
outline: none;
}
.vp-doc h1 {
.vp-doc h1:not(:where(.not-docs *)) {
letter-spacing: -0.02em;
line-height: 40px;
font-size: 28px;
}
.vp-doc h2 {
.vp-doc h2:not(:where(.not-docs *)) {
/* margin: 48px 0 16px; */
margin: 16px 0 16px;
border-top: 1px solid var(--vp-c-divider);
@ -37,14 +37,14 @@
font-size: 24px;
}
.vp-doc h3 {
.vp-doc h3:not(:where(.not-docs *)) {
margin: 32px 0 0;
letter-spacing: -0.01em;
line-height: 28px;
font-size: 20px;
}
.vp-doc .header-anchor {
.vp-doc .header-anchor:not(:where(.not-docs *)) {
position: absolute;
top: 0;
left: 0;
@ -58,7 +58,7 @@
opacity 0.25s;
}
.vp-doc .header-anchor:before {
.vp-doc .header-anchor:before:not(:where(.not-docs *)) {
content: var(--vp-header-anchor-symbol);
}
@ -78,14 +78,14 @@
}
@media (min-width: 768px) {
.vp-doc h1 {
.vp-doc h1:not(:where(.not-docs *)) {
letter-spacing: -0.02em;
line-height: 40px;
font-size: 32px;
}
}
.vp-doc h2 .header-anchor {
.vp-doc h2 .header-anchor:not(:where(.not-docs *)) {
top: 24px;
}
@ -93,30 +93,30 @@
* Paragraph and inline elements
* -------------------------------------------------------------------------- */
/* .vp-doc p,
.vp-doc summary {
.vp-doc p:not(:where(.not-docs *)),
.vp-doc summary:not(:where(.not-docs *)) {
margin: 16px 0;
}
.vp-doc p {
.vp-doc p:not(:where(.not-docs *)) {
line-height: 28px;
}
.vp-doc blockquote {
.vp-doc blockquote:not(:where(.not-docs *)) {
margin: 16px 0;
border-left: 2px solid var(--vp-c-divider);
padding-left: 16px;
transition: border-color 0.5s;
}
.vp-doc blockquote > p {
.vp-doc blockquote > p:not(:where(.not-docs *)) {
margin: 0;
font-size: 16px;
color: var(--vp-c-text-2);
transition: color 0.5s;
}
.vp-doc a {
.vp-doc a:not(:where(.not-docs *)) {
font-weight: 500;
color: var(--vp-c-brand-1);
text-decoration: underline;
@ -126,38 +126,38 @@
opacity 0.25s;
}
.vp-doc a:hover {
.vp-doc a:hover:not(:where(.not-docs *)) {
color: var(--vp-c-brand-2);
}
.vp-doc strong {
.vp-doc strong:not(:where(.not-docs *)) {
font-weight: 600;
} */
}
/**
* Lists
* -------------------------------------------------------------------------- */
.vp-doc ul:not(:where(.preview *)),
.vp-doc ol:not(:where(.preview *)) {
.vp-doc ul:not(:where(.not-docs *)),
.vp-doc ol:not(:where(.not-docs *)) {
padding-left: 1.25rem;
margin: 16px 0;
}
.vp-doc ul:not(:where(.preview *)) {
.vp-doc ul:not(:where(.not-docs *)) {
list-style: disc;
}
.vp-doc ol:not(:where(.preview *)) {
.vp-doc ol:not(:where(.not-docs *)) {
list-style: decimal;
}
.vp-doc li + li:not(:where(.preview *)) {
.vp-doc li + li:not(:where(.not-docs *)) {
margin-top: 8px;
}
.vp-doc li > ol:not(:where(.preview *)),
.vp-doc li > ul:not(:where(.preview *)) {
.vp-doc li > ol:not(:where(.not-docs *)),
.vp-doc li > ul:not(:where(.not-docs *)) {
margin: 8px 0 0;
}
@ -247,7 +247,7 @@
/* inline code */
.vp-doc :not(pre, h1, h2, h3, h4, h5, h6) > code {
font-size: var(--vp-code-font-size);
font-size: 14px;
color: var(--vp-code-color);
}
@ -343,10 +343,10 @@
}
.vp-doc [class*='language-'] code .highlighted {
background-color: var(--vp-code-line-highlight-color);
background-color: hsl(var(--muted));
transition: background-color 0.5s;
margin: 0 -24px;
padding: 0 24px;
/* margin: 0 -24px;
padding: 0 24px; */
width: calc(100% + 2 * 24px);
display: inline-block;
}

View File

@ -0,0 +1,20 @@
---
title: About
description: Powered by amazing open source projects.
---
## About
[shadcn-svelte](https://shadcn-svelte.com) is a port of [shadcn/ui](https://ui.shadcn.com) for Svelte/SvelteKit. It's maintained by [huntabyte](https://twitter.com/huntabyte).
## Credits
- [shadcn](https://twitter.com/shadcn) - The brilliant mind behind the designs, methodology, and implementation of the original [shadcn/ui](https://ui.shadcn.com).
- [Radix Vue](https://radix-vue.com) - The headless components that power this project.
- [Radix UI](https://radix-ui.com) - The headless components and examples that the original [shadcn/ui](https://ui.shadcn.com) was built on.
- [Shu Ding](https://shud.in) - The typography style is adapted from his work on Nextra.
- [Cal](https://cal.com) - Where shad copied the styles for the first component: the `Button`.
## License
MIT © [shadcn](https://shadcn.com) & [radix-vue](https://github.com/radix-vue)

View File

@ -0,0 +1,3 @@
---
title: Changelog
---

View File

@ -0,0 +1,101 @@
---
title: CLI
description: Use the CLI to add components to your project.
---
## init
Use the `init` command to initialize dependencies for a new project.
The `init` command installs dependencies, adds the `cn` util, configures `tailwind.config.cjs`, and creates CSS variables for the project.
```bash
npx shadcn-vue@latest init
```
You will be asked a few questions to configure `components.json`:
```txt showLineNumbers
Which style would you like to use? Default
Which color would you like to use as base color? Slate
Where is your global CSS file? src/app.postcss
Where is your tailwind.config.[cjs|js|ts] located? tailwind.config.js
Configure the import alias for components: $lib/components
Configure the import alias for utils: $lib/utils
```
### Options
```txt
Usage: shadcn-vue init [options]
initialize your project and install dependencies
Options:
-y, --yes skip confirmation prompt. (default: false)
-c, --cwd <cwd> the working directory. (default: the current directory)
-h, --help display help for command
```
## add
Use the `add` command to add components and dependencies to your project.
```bash
npx shadcn-vue@latest add [component]
```
You will be presented with a list of components to choose from:
```txt
Which components would you like to add? Space to select. Return to submit.
◯ accordion
◯ alert
◯ alert-dialog
◯ aspect-ratio
◯ avatar
◯ badge
◯ button
◯ card
◯ checkbox
◯ collapsible
```
### Options
```txt
Usage: shadcn-vue add [options] [components...]
add components to your project
Arguments:
components name of components
Options:
--nodep disable adding & installing dependencies (advanced) (default: false)
-y, --yes Skip confirmation prompt. (default: false)
-o, --overwrite overwrite existing files. (default: false)
-c, --cwd <cwd> the working directory. (default: the current directory)
-p, --path <path> the path to add the component to.
-h, --help display help for command
```
## update
Use the `update` command to update components in your project. This will overwrite any modifications you've made to the components, so be sure to commit your changes before running this command.
We plan on improving this command in the future to improve the update experience.
```txt
Usage: shadcn-vue update [options] [components...]
update components in your project
Arguments:
components name of components
Options:
-c, --cwd <cwd> the working directory. (default: the current directory)
-h, --help display help for command
```

View File

@ -0,0 +1,123 @@
---
title: components.json
description: Configuration for your project.
---
The `components.json` file holds configuration for your project.
We use it to understand how your project is set up and how to generate components customized for your project.
<Callout class="mt-6">
Note: The <code>components.json</code> file is optional and **only required if you're
using the CLI** to add components to your project. If you're using the copy
and paste method, you don't need this file.
</Callout>
You can create a `components.json` file in your project by running the following command:
```bash
npx shadcn-svelte@latest init
```
See the [CLI section](/docs/cli) for more information.
## $schema
You can see the JSON Schema for `components.json` [here](https://shadcn-svelte.com/schema.json).
```json title="components.json"
{
"$schema": "https://shadcn-svelte.com/schema.json"
}
```
## style
The style for your components. **This cannot be changed after initialization.**
<!-- eslint-skip -->
```json title="components.json"
{
"style": "default" | "new-york"
}
```
<ComponentPreview name="card-with-form">
<div />
</ComponentPreview>
## tailwind
Configuration to help the CLI understand how Tailwind CSS is set up in your project.
See the [installation section](/docs/installation) for how to set up Tailwind CSS.
### tailwind.config
Path to where your `tailwind.config.js` file is located.
<!-- eslint-skip -->
```json title="components.json"
{
"tailwind": {
"config": "tailwind.config.js" | "tailwind.config.ts"
}
}
```
### tailwind.css
Path to the CSS file that imports Tailwind CSS into your project.
```json title="components.json"
{
"tailwind": {
"css": "src/app.postcss"
}
}
```
### tailwind.baseColor
This is used to generate the default color palette for your components. **This cannot be changed after initialization.**
<!-- eslint-skip -->
```json title="components.json"
{
"tailwind": {
"baseColor": "gray" | "neutral" | "slate" | "stone" | "zinc"
}
}
```
## aliases
The CLI uses these values and the `alias` config from your `svelte.config.js` file to place generated components in the correct location.
Path aliases have to be set up in your `svelte.config.js` file.
### aliases.utils
Import alias for your utility functions.
```json title="components.json"
{
"aliases": {
"utils": "$lib/utils"
}
}
```
### aliases.components
Import alias for your components.
```json title="components.json"
{
"aliases": {
"components": "$lib/components"
}
}
```

View File

@ -0,0 +1,21 @@
---
title: Figma
description: Every component recreated in Figma. With customizable props, typography and icons.
---
<script setup>
import { AspectRatio } from '@/lib/registry/default/ui/aspect-ratio';
</script>
The Figma UI Kit is open sourced by [Pietro Schirano](https://twitter.com/skirano).
<AspectRatio :ratio="16 / 9" class="w-full mt-4">
<iframe
src="https://embed.figma.com/file/1203061493325953101/hf_embed?community_viewer=true&embed_host=shadcn&hub_file_id=1203061493325953101&kind=&viewer=1"
class="h-full w-full overflow-hidden rounded-lg border bg-muted"
/>
</AspectRatio>
## Grab a copy
https://www.figma.com/community/file/1203061493325953101

View File

@ -0,0 +1,415 @@
---
title: Installation
description: How to install dependencies and structure your app.
---
<script setup>
import { Alert, AlertDescription } from "@/lib/registry/default/ui/alert";
</script>
Unlike the original [shadcn/ui](https://ui.shadcn.com) for React, where the full components can exist in a single file, components in this port are split into multiple files. This is because Svelte doesn't support defining multiple components in a single file, so utilizing the CLI to add components will be the optimal approach.
The CLI will create a folder for _each_ component, which will sometimes just contain a single Svelte file, and in other times, multiple files. Within each folder, there will be an `index.ts` file that exports the component(s), so you can import them from a single file.
For example, the Accordion component is split into four `.svelte` files:
- `Accordion.svelte`
- `AccordionContent.svelte`
- `AccordionItem.svelte`
- `AccordionTrigger.svelte`
They can then be imported from the `accordion/index.ts` file like so:
```ts
import * as Accordion from '$lib/components/ui/accordion'
// or
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger
} from '$lib/components/ui/accordion'
```
Regardless of the import approach you take, the components will be tree-shaken by Rollup, so you don't have to worry about unused components being bundled into your app.
## New Project
<Steps>
### Create project
Use the SvelteKit CLI to create a new project.
```bash
npm create svelte@latest my-app
```
### Add TailwindCSS
Use the `svelte-add` CLI to add Tailwind CSS to your project.
```bash
npx svelte-add@latest tailwindcss
```
### Install dependencies
```bash
npm install
```
### Run the CLI
```bash
npx shadcn-svelte@latest init
```
### Configure components.json
You will be asked a few questions to configure `components.json`:
```txt showLineNumbers
Which style would you like to use? Default
Which color would you like to use as base color? Slate
Where is your global CSS file? src/app.postcss
Where is your tailwind.config.[cjs|js|ts] located? tailwind.config.js
Configure the import alias for components: $lib/components
Configure the import alias for utils: $lib/utils
```
### Setup path aliases
If you changed the path aliases from the default, you'll also need to update your `svelte.config.js` file to include those aliases.
```js title="svelte.config.js"
const config = {
// ... other config
kit: {
// ... other config
alias: {
'$lib': './src/lib',
'$lib/*': './src/lib/*'
}
}
}
```
</Steps>
## Manual Installation
<Steps>
### Create project
Use the SvelteKit CLI to create a new project.
```bash
npm create svelte@latest my-app
```
### Add Tailwind
Use the `svelte-add` CLI to add Tailwind CSS to your project.
```bash
npx svelte-add@latest tailwindcss
```
### Add dependencies
Add the following dependencies to your project:
```bash
npm install tailwind-variants clsx tailwind-merge
```
### Configure tailwind.config.js
This is what this project's `tailwind.config.js` file looks like:
```javascript title="tailwind.config.js"
import { fontFamily } from "tailwindcss/defaultTheme";
/** @type {import('tailwindcss').Config} */
const config = {
darkMode: ["class"],
content: ["./src/**/*.{html,js,svelte,ts}"],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px"
}
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))"
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))"
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))"
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))"
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))"
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))"
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))"
}
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)"
},
fontFamily: {
sans: ["Inter", ...fontFamily.sans]
}
}
},
plugins: [tailwindcssAnimate]
};
export default config;
```
Feel free to add or modify as needed to suit your project.
### Configure styles
Add the following to your `src/app.postcss` file. You can learn more about using CSS variables for theming in the [theming section](/docs/theming).
```css title="src/app.postcss"
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 47.4% 11.2%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--card: 0 0% 100%;
--card-foreground: 222.2 47.4% 11.2%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 92% 38%;
--destructive-foreground: 210 40% 98%;
--ring: 215 20.2% 65.1%;
--radius: 0.5rem;
}
.dark {
--background: 224 71% 4%;
--foreground: 213 31% 91%;
--muted: 223 47% 11%;
--muted-foreground: 215.4 16.3% 56.9%;
--accent: 216 34% 17%;
--accent-foreground: 210 40% 98%;
--popover: 224 71% 4%;
--popover-foreground: 215 20.2% 65.1%;
--border: 216 34% 17%;
--input: 216 34% 17%;
--card: 224 71% 4%;
--card-foreground: 213 31% 91%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 1.2%;
--secondary: 222.2 47.4% 11.2%;
--secondary-foreground: 210 40% 98%;
--destructive: 359 51% 48%;
--destructive-foreground: 210 40% 98%;
--ring: 216 34% 17%;
--radius: 0.5rem;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
font-feature-settings: "rlig" 1, "calt" 1;
}
}
```
### Configure utils
You'll want to create a `cn` helper to make it easier to conditionally add Tailwind CSS classes. Additionally, you'll want to add the custom transition that is used by various components.
```ts title="src/lib/utils.ts"
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
import { cubicOut } from 'svelte/easing'
import type { TransitionConfig } from 'svelte/transition'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
interface FlyAndScaleParams {
y?: number
x?: number
start?: number
duration?: number
}
export function flyAndScale(node: Element,
params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }): TransitionConfig {
const style = getComputedStyle(node)
const transform = style.transform === 'none' ? '' : style.transform
const scaleConversion = (
valueA: number,
scaleA: [number, number],
scaleB: [number, number]
) => {
const [minA, maxA] = scaleA
const [minB, maxB] = scaleB
const percentage = (valueA - minA) / (maxA - minA)
const valueB = percentage * (maxB - minB) + minB
return valueB
}
const styleToString = (
style: Record<string, number | string | undefined>
): string => {
return Object.keys(style).reduce((str, key) => {
if (style[key] === undefined)
return str
return `${str + key}:${style[key]};`
}, '')
}
return {
duration: params.duration ?? 200,
delay: 0,
css: (t) => {
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0])
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0])
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1])
return styleToString({
transform:
`${transform
}translate3d(${
x
}px, ${
y
}px, 0) scale(${
scale
})`,
opacity: t
})
},
easing: cubicOut
}
}
```
### Import styles to your app
Create `src/routes/+layout.svelte` and import the styles:
```svelte title="src/routes/+layout.svelte"
<script lang="ts">
import "../app.postcss";
</script>
<slot />
```
</Steps>
## Icons
This project uses icons from [Lucide](https://lucide.dev/) for the `default` style, and [Radix](https://radix-ui.com/icons) for the `new-york` style, but feel free to use any icon library.
## App structure
Here's a recommended, but not required app structure:
```txt {4-11,15,19}
src
├── lib
│ ├── components
│ │ ├── ui
│ │ │ ├── alert-dialog
│ │ │ │ ├── index.ts
│ │ │ │ └── alert.svelte
│ │ │ ├── button
│ │ │ │ ├── index.ts
│ │ │ │ └── button.svelte
│ │ │ └── ...
│ │ ├── navigation.svelte
│ │ ├── page-header.svelte
│ │ └── ...
│ └── utils.ts
├── routes
│ ├── +page.svelte
│ └── +layout.svelte
├── app.postcss
```
- Place the UI components in the `lib/components/ui` folder.
- The rest of the components such as `<PageHeader />` and `<Navigation />` are placed in the `lib/components` folder.
- The `lib/utils.ts` file is where you can define the `cn` helper.
- The `app.postcss` file contains the global CSS.
That's it. You can now start adding components to your project.

View File

@ -0,0 +1,63 @@
---
title: Introduction
description: Re-usable components built with Radix Vue, and Tailwind CSS.
---
<script setup >
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'
</script>
An unofficial, community-led [Vue](https://vuejs.org/) port of [shadcn/ui](https://ui.shadcn.com). We are not affiliated with [shadcn](https://twitter.com/shadcn), but we did get his blessing before creating a Vue version of his work. This project was born out of the need for a similar project for the Vue ecosystem.
This is **NOT** a component library. It's a collection of re-usable components that you can copy and paste or use the CLI to add to your apps.
**What do you mean not a component library?**
It means you do not install it as a dependency. It is not available or distributed via npm, with no plans to publish it.
Pick the components you need. Use the CLI to automatically add the components, or copy and paste the code into your project and customize to your needs. The code is yours.
_Use this as a reference to build your own component libraries._
## FAQ
<AccordionRoot multiple>
<AccordionItem value="faq-1">
<AccordionTrigger>
Why not packaged as a dependency?
</AccordionTrigger>
<AccordionContent>
The idea behind this is to give you ownership and control over the code, allowing you to decide how the components are built and styled.
Start with some sensible defaults, then customize the components to your needs.
One of the drawback of packaging the components in an npm package is that the style is coupled with the implementation. _The design of your components should be separate from their implementation._
</AccordionContent>
</AccordionItem>
<AccordionItem value="faq-2">
<AccordionTrigger>
Which frameworks are supported?
</AccordionTrigger>
<AccordionContent>
This port is built to be used with Vue/Nuxt.
</AccordionContent>
</AccordionItem>
<AccordionItem value="faq-3">
<AccordionTrigger>
Can I use this in my project?
</AccordionTrigger>
<AccordionContent>
Yes. Free to use for personal and commercial projects. No attribution required.
But let us know if you do use it. We'd love to see what you build with it.
</AccordionContent>
</AccordionItem>
</AccordionRoot>

View File

@ -0,0 +1,184 @@
---
title: Theming
description: Use CSS Variables to customize the look and feel of your application.
---
We use CSS variables for styling. This allows you to easily change the colors of components without having to update class names.
**CSS variables must be defined without the color space function**. See the [Tailwind CSS documentation](https://tailwindcss.com/docs/customizing-colors#using-css-variables) for more information.
## Hex -> Color Channel
You can use this tool to convert your HEX color to HSL without the color space function. Simply add your color in hex format, copy one of the generated values, then add them to the CSS variable.
<!-- <HexToChannels /> -->
## Convention
We use a simple `background` and `foreground` convention for colors. The `background` variable is used for the background color of the component and the `foreground` variable is used for the text color.
<Callout>
The `background` suffix can be omitted if the variable is used for the background color of the component.
</Callout>
Given the following CSS variables:
```css
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
```
The `background` color of the following component will be `hsl(var(--primary))` and the `foreground` color will be `hsl(var(--primary-foreground))`.
```svelte
<div class="bg-primary text-primary-foreground">Hello</div>
```
## CSS Variables
Here's the list of variables available for customization:
```css title="Default background color of <body />...etc"
--background: 0 0% 100%;
--foreground: 222.2 47.4% 11.2%;
```
```css title="Muted backgrounds such as <TabsList />, <Skeleton /> and <Switch />"
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
```
```css title="Background color for <Card />"
--card: 0 0% 100%;
--card-foreground: 222.2 47.4% 11.2%;
```
```css title="Background color for popovers such as <DropdownMenu />, <HoverCard />, <Popover />"
--popover: 0 0% 100%;
--popover-foreground: 222.2 47.4% 11.2%;
```
```css title="Default border color"
--border: 214.3 31.8% 91.4%;
```
```css title="Border color for inputs such as <Input />, <Select />, <Textarea />"
--input: 214.3 31.8% 91.4%;
```
```css title="Primary colors for <Button />"
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
```
```css title="Secondary colors for <Button />"
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
```
```css title="Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc"
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
```
```css title="Used for destructive actions such as <Button variant='destructive'>"
--destructive: 0 100% 50%;
--destructive-foreground: 210 40% 98%;
```
```css title="Used for focus ring"
--ring: 215 20.2% 65.1%;
```
```css title="Border radius for card, input and buttons"
--radius: 0.5rem;
```
## Default
The following is the default color palette used by the components.
```css title="src/app.postcss"
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--ring: 240 5% 64.9%;
--radius: 0.5rem;
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 85.7% 97.3%;
--ring: 240 3.7% 15.9%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
font-feature-settings: "rlig" 1, "calt" 1;
}
}
```

View File

@ -0,0 +1,115 @@
---
title: Typography
description: Styles for headings, paragraphs, lists...etc
component: true
---
<ComponentPreview name="TypographyDemo">
<div/>
</ComponentPreview>
## h1
<ComponentPreview name="TypographyH1">
<div/>
</ComponentPreview>
## h2
<ComponentPreview name="TypographyH2">
<div/>
</ComponentPreview>
## h3
<ComponentPreview name="TypographyH3">
<div/>
</ComponentPreview>
## h4
<ComponentPreview name="TypographyH4">
<div/>
</ComponentPreview>
## p
<ComponentPreview name="TypographyP">
<div/>
</ComponentPreview>
## blockquote
<ComponentPreview name="TypographyBlockquote">
<div/>
</ComponentPreview>
## table
<ComponentPreview name="TypographyTable">
<div/>
</ComponentPreview>
## list
<ComponentPreview name="TypographyList">
<div/>
</ComponentPreview>
## Inline code
<ComponentPreview name="TypographyInlineCode">
<div/>
</ComponentPreview>
## Lead
<ComponentPreview name="TypographyLead">
<div/>
</ComponentPreview>
## Large
<ComponentPreview name="TypographyLarge">
<div/>
</ComponentPreview>
## Small
<ComponentPreview name="TypographySmall">
<div/>
</ComponentPreview>
## Muted
<ComponentPreview name="TypographyMuted">
<div/>
</ComponentPreview>

View File

@ -0,0 +1,6 @@
<template>
<blockquote class="mt-6 border-l-2 pl-6 italic">
"After all," he said, "everyone enjoys a good joke, so it's only fair that
they should pay for the privilege."
</blockquote>
</template>

View File

@ -0,0 +1,136 @@
<template>
<div>
<h1 class="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl">
The Joke Tax Chronicles
</h1>
<p class="leading-7 [&:not(:first-child)]:mt-6">
Once upon a time, in a far-off land, there was a very lazy king who
spent all day lounging on his throne. One day, his advisors came to him
with a problem: the kingdom was running out of money.
</p>
<h2
class="mt-10 scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0"
>
The King's Plan
</h2>
<p class="leading-7 [&:not(:first-child)]:mt-6">
The king thought long and hard, and finally came up with{" "}
<!-- svelte-ignore a11y-invalid-attribute -->
<a
href="#"
class="font-medium text-primary underline underline-offset-4"
>
a brilliant plan
</a>
: he would tax the jokes in the kingdom.
</p>
<blockquote class="mt-6 border-l-2 pl-6 italic">
"After all," he said, "everyone enjoys a good joke, so it's only fair
that they should pay for the privilege."
</blockquote>
<h3 class="mt-8 scroll-m-20 text-2xl font-semibold tracking-tight">
The Joke Tax
</h3>
<p class="leading-7 [&:not(:first-child)]:mt-6">
The king's subjects were not amused. They grumbled and complained, but
the king was firm:
</p>
<ul class="my-6 ml-6 list-disc [&>li]:mt-2">
<li>1st level of puns: 5 gold coins</li>
<li>2nd level of jokes: 10 gold coins</li>
<li>3rd level of one-liners : 20 gold coins</li>
</ul>
<p class="leading-7 [&:not(:first-child)]:mt-6">
As a result, people stopped telling jokes, and the kingdom fell into a
gloom. But there was one person who refused to let the king's
foolishness get him down: a court jester named Jokester.
</p>
<h3 class="mt-8 scroll-m-20 text-2xl font-semibold tracking-tight">
Jokester's Revolt
</h3>
<p class="leading-7 [&:not(:first-child)]:mt-6">
Jokester began sneaking into the castle in the middle of the night and
leaving jokes all over the place: under the king's pillow, in his soup,
even in the royal toilet. The king was furious, but he couldn't seem to
stop Jokester.
</p>
<p class="leading-7 [&:not(:first-child)]:mt-6">
And then, one day, the people of the kingdom discovered that the jokes
left by Jokester were so funny that they couldn't help but laugh. And
once they started laughing, they couldn't stop.
</p>
<h3 class="mt-8 scroll-m-20 text-2xl font-semibold tracking-tight">
The People's Rebellion
</h3>
<p class="leading-7 [&:not(:first-child)]:mt-6">
The people of the kingdom, feeling uplifted by the laughter, started to
tell jokes and puns again, and soon the entire kingdom was in on the
joke.
</p>
<div class="my-6 w-full overflow-y-auto">
<table class="w-full">
<thead>
<tr class="m-0 border-t p-0 even:bg-muted">
<th
class="border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right"
>
King's Treasury
</th>
<th
class="border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right"
>
People's happiness
</th>
</tr>
</thead>
<tbody>
<tr class="m-0 border-t p-0 even:bg-muted">
<td
class="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right"
>
Empty
</td>
<td
class="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right"
>
Overflowing
</td>
</tr>
<tr class="m-0 border-t p-0 even:bg-muted">
<td
class="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right"
>
Modest
</td>
<td
class="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right"
>
Satisfied
</td>
</tr>
<tr class="m-0 border-t p-0 even:bg-muted">
<td
class="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right"
>
Full
</td>
<td
class="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right"
>
Ecstatic
</td>
</tr>
</tbody>
</table>
</div>
<p class="leading-7 [&:not(:first-child)]:mt-6">
The king, seeing how much happier his subjects were, realized the error
of his ways and repealed the joke tax. Jokester was declared a hero, and
the kingdom lived happily ever after.
</p>
<p class="leading-7 [&:not(:first-child)]:mt-6">
The moral of the story is: never underestimate the power of a good laugh
and always be careful of bad ideas.
</p>
</div>
</template>

View File

@ -0,0 +1,5 @@
<template>
<h1 class="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl">
Taxing Laughter: The Joke Tax Chronicles
</h1>
</template>

View File

@ -0,0 +1,7 @@
<template>
<h2
class="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0"
>
The People of the Kingdom
</h2>
</template>

View File

@ -0,0 +1,5 @@
<template>
<h3 class="scroll-m-20 text-2xl font-semibold tracking-tight">
The Joke Tax
</h3>
</template>

View File

@ -0,0 +1,5 @@
<template>
<h4 class="scroll-m-20 text-xl font-semibold tracking-tight">
People stopped telling jokes
</h4>
</template>

View File

@ -0,0 +1,7 @@
<template>
<code
class="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold"
>
radix-vue
</code>
</template>

View File

@ -0,0 +1,5 @@
<template>
<div class="text-lg font-semibold">
Are you sure absolutely sure?
</div>
</template>

View File

@ -0,0 +1,6 @@
<template>
<p class="text-xl text-muted-foreground">
A modal dialog that interrupts the user with important content and expects a
response.
</p>
</template>

View File

@ -0,0 +1,7 @@
<template>
<ul class="my-6 ml-6 list-disc [&>li]:mt-2">
<li>1st level of puns: 5 gold coins</li>
<li>2nd level of jokes: 10 gold coins</li>
<li>3rd level of one-liners : 20 gold coins</li>
</ul>
</template>

View File

@ -0,0 +1,5 @@
<template>
<p class="text-sm text-muted-foreground">
Enter your email address.
</p>
</template>

View File

@ -0,0 +1,6 @@
<template>
<p class="leading-7 [&:not(:first-child)]:mt-6">
The king, seeing how much happier his subjects were, realized the error of
his ways and repealed the joke tax.
</p>
</template>

View File

@ -0,0 +1,5 @@
<template>
<small class="text-sm font-medium leading-none">
Email address
</small>
</template>

View File

@ -0,0 +1,58 @@
<template>
<div class="my-6 w-full overflow-y-auto">
<table class="w-full">
<thead>
<tr class="m-0 border-t p-0 even:bg-muted">
<th
class="border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right"
>
King's Treasury
</th>
<th
class="border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right"
>
People's happiness
</th>
</tr>
</thead>
<tbody>
<tr class="m-0 border-t p-0 even:bg-muted">
<td
class="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right"
>
Empty
</td>
<td
class="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right"
>
Overflowing
</td>
</tr>
<tr class="m-0 border-t p-0 even:bg-muted">
<td
class="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right"
>
Modest
</td>
<td
class="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right"
>
Satisfied
</td>
</tr>
<tr class="m-0 border-t p-0 even:bg-muted">
<td
class="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right"
>
Full
</td>
<td
class="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right"
>
Ecstatic
</td>
</tr>
</tbody>
</table>
</div>
</template>

View File

@ -1,14 +1,17 @@
<script setup lang="ts">
import {
AccordionRoot,
type AccordionRootEmits,
type AccordionRootProps,
} from 'radix-vue'
import { useEmitAsProps } from '@/lib/utils'
const props = defineProps<AccordionRootProps>()
const emits = defineEmits<AccordionRootEmits>()
</script>
<template>
<AccordionRoot v-bind="props" class="accordion">
<AccordionRoot v-bind="{ ...props, ...useEmitAsProps(emits) }">
<slot />
</AccordionRoot>
</template>

View File

@ -10,7 +10,7 @@ const props = defineProps<AccordionContentProps & { class?: string }>()
v-bind="props"
:class="
cn(
'overflow-hidden text-sm transition-all data-[state=open]:animate-accordion-down data-[state=closed]:animate-accordion-up',
'overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',
props.class,
)
"

View File

@ -8,7 +8,7 @@ const props = defineProps<AccordionItemProps & { class?: string }>()
<template>
<AccordionItem
v-bind="props"
:class="cn('border-b text-foreground border-border', props.class ?? '')"
:class="cn('border-b', props.class ?? '')"
>
<slot />
</AccordionItem>

View File

@ -16,14 +16,14 @@ const props = defineProps<AccordionTriggerProps & { class?: string }>()
v-bind="props"
:class="
cn(
'flex flex-1 group items-center justify-between py-4 font-medium transition-all hover:underline',
'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
props.class,
)
"
>
<slot />
<ChevronDown
class="w-4 h-4 shrink-0 transition-transform duration-300 group-data-[state=open]:rotate-180"
class="h-4 w-4 shrink-0 transition-transform duration-200"
/>
</AccordionTrigger>
</AccordionHeader>