Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
96d5694
feat(tokens): sync espresso v2 design tokens from Figma + rebuild fou…
netchampfaris May 21, 2026
c950fd2
docs(foundations): use gray for corner radius example tiles
netchampfaris May 21, 2026
67c2eb2
docs(foundations): square palette swatches
netchampfaris May 22, 2026
3aea83b
fix(docs): restore shiki colors in dev
netchampfaris May 22, 2026
30ce4f4
feat(foundations): align Button with Figma + add foundations spec
netchampfaris May 23, 2026
3011009
docs(button): replace prop stories with Figma examples + playground
netchampfaris May 23, 2026
8b257c6
feat(components): refine badge and button examples
netchampfaris May 23, 2026
d124049
feat(tokens): add elevation and focus-ring effect tokens
netchampfaris May 24, 2026
984cf76
feat(icon): add shared Icon renderer
netchampfaris May 24, 2026
011202f
feat(pill): add Pill primitive
netchampfaris May 24, 2026
0d122ea
refactor(tab-buttons)!: render Pill, support route/href, drop Button …
netchampfaris May 24, 2026
bdfc562
chore: regenerate components.d.ts and propsgen output
netchampfaris May 24, 2026
9235432
refactor(docs): generic ComponentPlayground for component builders
netchampfaris May 24, 2026
eb9c0d1
fix(pill): redesign vertical underline tab indicator
netchampfaris May 24, 2026
1ffc0db
refactor(focus-ring): adopt focus-ring utilities across components
netchampfaris May 24, 2026
77a86aa
docs(tab-buttons): refresh story examples with realistic nav content
netchampfaris May 24, 2026
f23f8eb
chore(tab-buttons): drop unused TabButtons.story.vue
netchampfaris May 24, 2026
ab50518
docs(foundations): rename pages, refresh palette/semantic rendering
netchampfaris May 24, 2026
7b9c26c
refactor(elevation): use one shadow set for both themes
netchampfaris May 24, 2026
0fd252c
fix(docs/playground): sync code snippet theme with dark mode
netchampfaris May 24, 2026
3bc115c
docs(foundations): refresh radius + typography token displays
netchampfaris May 25, 2026
002efa1
chore(tokens): align tailwind/tokens.js exports with Figma-generated …
netchampfaris May 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@ docs/content/docs/components/*.md
coverage
.nyc_output

cypress/screenshots
cypress/screenshots

.tmp-screenshots
13 changes: 6 additions & 7 deletions components.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/* eslint-disable */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable

export {}

/* prettier-ignore */
Expand All @@ -27,16 +30,12 @@ declare module 'vue' {
Checkbox: typeof import('./src/components/Checkbox/Checkbox.vue')['default']
CircularProgressBar: typeof import('./src/components/CircularProgressBar/CircularProgressBar.vue')['default']
'CircularProgressBar.story': typeof import('./src/components/CircularProgressBar/CircularProgressBar.story.vue')['default']
Clearable: typeof import('./src/components/Rating/stories/Clearable.vue')['default']
CodeBlockComponent: typeof import('./src/components/TextEditor/components/CodeBlockComponent.vue')['default']
Combobox: typeof import('./src/components/Combobox/Combobox.vue')['default']
ComboboxResults: typeof import('./src/components/Combobox/ComboboxResults.vue')['default']
CommandPalette: typeof import('./src/components/CommandPalette/CommandPalette.vue')['default']
CommandPaletteItem: typeof import('./src/components/CommandPalette/CommandPaletteItem.vue')['default']
ConfirmDialog: typeof import('./src/components/ConfirmDialog.vue')['default']
CustomColor: typeof import('./src/components/Rating/stories/CustomColor.vue')['default']
CustomIcon: typeof import('./src/components/Rating/stories/CustomIcon.vue')['default']
CustomSlot: typeof import('./src/components/Rating/stories/CustomSlot.vue')['default']
DatePicker: typeof import('./src/components/DatePicker/DatePicker.vue')['default']
DateRangePicker: typeof import('./src/components/DatePicker/DateRangePicker.vue')['default']
DateTimePicker: typeof import('./src/components/DatePicker/DateTimePicker.vue')['default']
Expand All @@ -63,8 +62,7 @@ declare module 'vue' {
FormLabel: typeof import('./src/components/FormLabel.vue')['default']
FrappeUIProvider: typeof import('./src/components/Provider/FrappeUIProvider.vue')['default']
FunnelChart: typeof import('./src/components/Charts/FunnelChart.vue')['default']
HalfStep: typeof import('./src/components/Rating/stories/HalfStep.vue')['default']
HalfStepCustomColor: typeof import('./src/components/Rating/stories/HalfStepCustomColor.vue')['default']
Icon: typeof import('./src/components/Icon/Icon.vue')['default']
IframeNodeView: typeof import('./src/components/TextEditor/extensions/iframe/IframeNodeView.vue')['default']
ImageGroupNodeView: typeof import('./src/components/TextEditor/extensions/image-group/ImageGroupNodeView.vue')['default']
ImageGroupUploadDialog: typeof import('./src/components/TextEditor/extensions/image-group/ImageGroupUploadDialog.vue')['default']
Expand Down Expand Up @@ -111,6 +109,7 @@ declare module 'vue' {
OptionIcon: typeof import('./src/components/shared/selection/OptionIcon.vue')['default']
Password: typeof import('./src/components/Password/Password.vue')['default']
PickerShell: typeof import('./src/components/shared/picker/PickerShell.vue')['default']
Pill: typeof import('./src/components/Pill/Pill.vue')['default']
Popover: typeof import('./src/components/Popover/Popover.vue')['default']
Progress: typeof import('./src/components/Progress/Progress.vue')['default']
Rating: typeof import('./src/components/Rating/Rating.vue')['default']
Expand Down
5 changes: 4 additions & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ export default defineConfig({
dark: 'tokyo-night',
light: 'github-light',
},
codeTransformers: [toClass],
// transformerStyleToClass flushes its CSS only at buildEnd, so in dev
// the generated shiki.css stays empty and code blocks lose colors —
// skip it in dev and let shiki emit inline --shiki-light/dark styles.
codeTransformers: isDev ? [] : [toClass],
config(md) {
md.use(componentTransformer)
},
Expand Down
16 changes: 12 additions & 4 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import type { Theme } from 'vitepress'
import "../../../src/fonts/Inter/inter.css"
import "../../css/style.css"
import "../../css/shiki.css"
import '../../../src/fonts/Inter/inter.css'
import '../../css/style.css'
import '../../css/shiki.css'
import Demo from '../../components/Docs/Demo.vue'
import ButtonBuilder from '../../components/Docs/ButtonBuilder.vue'
import BadgeBuilder from '../../components/Docs/BadgeBuilder.vue'
import PillBuilder from '../../components/Docs/PillBuilder.vue'
import TabButtonsBuilder from '../../components/Docs/TabButtonsBuilder.vue'
import Layout from '../../components/Layout.vue'

if (process.env.NODE_ENV === 'production') {
import.meta.glob('../components/**/stories/*.vue', { eager: true })
}

export default {
Layout,
Layout,
enhanceApp({ app, router, siteData }) {
app.component('ComponentPreview', Demo)
app.component('ButtonBuilder', ButtonBuilder)
app.component('BadgeBuilder', BadgeBuilder)
app.component('PillBuilder', PillBuilder)
app.component('TabButtonsBuilder', TabButtonsBuilder)
},
} satisfies Theme
79 changes: 79 additions & 0 deletions docs/components/Docs/BadgeBuilder.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<script setup lang="ts">
import { Badge } from 'frappe-ui'
import ComponentPlayground, { type Knob } from './ComponentPlayground.vue'

const knobs: Knob[] = [
{ name: 'label', type: 'text', default: 'Gamma' },
{
name: 'variant',
type: 'tabs',
default: 'solid',
options: [
{ label: 'solid', value: 'solid' },
{ label: 'subtle', value: 'subtle' },
{ label: 'outline', value: 'outline' },
{ label: 'ghost', value: 'ghost' },
],
},
{
name: 'theme',
type: 'tabs',
default: 'green',
options: [
{ label: 'gray', value: 'gray' },
{ label: 'blue', value: 'blue' },
{ label: 'green', value: 'green' },
{ label: 'amber', value: 'amber' },
{ label: 'red', value: 'red' },
{ label: 'violet', value: 'violet' },
],
},
{
name: 'size',
type: 'tabs',
default: 'lg',
options: [
{ label: 'sm', value: 'sm' },
{ label: 'md', value: 'md' },
{ label: 'lg', value: 'lg' },
],
},
{ name: 'prefix', type: 'switch', default: true },
{ name: 'suffix', type: 'switch', default: false },
]

function buildCode(v: Record<string, any>) {
const attrs = [
`variant="${v.variant}"`,
`theme="${v.theme}"`,
`size="${v.size}"`,
]
const slots: string[] = []
if (v.prefix) {
slots.push(' <template #prefix><span class="lucide-check" /></template>')
}
slots.push(` ${v.label}`)
if (v.suffix) {
slots.push(
' <template #suffix><span class="lucide-chevron-down" /></template>',
)
}
return ['<Badge', ...attrs.map((a) => ' ' + a), '>', ...slots, '</Badge>'].join('\n')
}
</script>

<template>
<ComponentPlayground :knobs="knobs" :code="buildCode">
<template #preview="{ values }">
<Badge :theme="values.theme" :variant="values.variant" :size="values.size">
<template v-if="values.prefix" #prefix>
<span class="lucide-check" />
</template>
{{ values.label }}
<template v-if="values.suffix" #suffix>
<span class="lucide-chevron-down" />
</template>
</Badge>
</template>
</ComponentPlayground>
</template>
97 changes: 97 additions & 0 deletions docs/components/Docs/ButtonBuilder.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<script setup lang="ts">
import { Button } from 'frappe-ui'
import ComponentPlayground, { type Knob } from './ComponentPlayground.vue'

const knobs: Knob[] = [
{ name: 'label', type: 'text', default: 'Save' },
{
name: 'variant',
type: 'tabs',
default: 'subtle',
options: [
{ label: 'solid', value: 'solid' },
{ label: 'subtle', value: 'subtle' },
{ label: 'outline', value: 'outline' },
{ label: 'ghost', value: 'ghost' },
],
},
{
name: 'theme',
type: 'tabs',
default: 'gray',
options: [
{ label: 'gray', value: 'gray' },
{ label: 'red', value: 'red' },
],
},
{
name: 'size',
type: 'tabs',
default: 'sm',
options: [
{ label: 'xs', value: 'xs' },
{ label: 'sm', value: 'sm' },
{ label: 'md', value: 'md' },
{ label: 'lg', value: 'lg' },
{ label: 'xl', value: 'xl' },
{ label: '2xl', value: '2xl' },
],
},
{
name: 'iconLeft',
type: 'switch',
default: false,
disabledWhen: (v) => v.icon,
},
{ name: 'icon', type: 'switch', default: false },
{
name: 'iconRight',
type: 'switch',
default: false,
disabledWhen: (v) => v.icon,
},
{ name: 'disabled', type: 'switch', default: false },
{ name: 'loading', type: 'switch', default: false },
]

function buildCode(v: Record<string, any>) {
const attrs = [
`variant="${v.variant}"`,
`theme="${v.theme}"`,
`size="${v.size}"`,
`label="${v.label}"`,
]
if (v.icon) {
attrs.push(`icon="lucide-plus"`)
} else {
if (v.iconLeft) attrs.push(`icon-left="lucide-plus"`)
if (v.iconRight) attrs.push(`icon-right="lucide-chevron-right"`)
}
if (v.disabled) attrs.push('disabled')
if (v.loading) attrs.push('loading')

return ['<Button', ...attrs.map((a) => ' ' + a), '/>'].join('\n')
}
</script>

<template>
<ComponentPlayground :knobs="knobs" :code="buildCode">
<template #preview="{ values }">
<Button
:theme="values.theme"
:variant="values.variant"
:size="values.size"
:label="values.label"
:icon-left="
!values.icon && values.iconLeft ? 'lucide-plus' : undefined
"
:icon="values.icon ? 'lucide-plus' : undefined"
:icon-right="
!values.icon && values.iconRight ? 'lucide-chevron-right' : undefined
"
:loading="values.loading"
:disabled="values.disabled"
/>
</template>
</ComponentPlayground>
</template>
Loading
Loading