Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
7a799e1
feat(tokens): sync espresso v2 design tokens from Figma + rebuild fou…
netchampfaris May 21, 2026
a289b00
docs(foundations): use gray for corner radius example tiles
netchampfaris May 21, 2026
4d3da15
docs(foundations): square palette swatches
netchampfaris May 22, 2026
c0a5244
fix(docs): restore shiki colors in dev
netchampfaris May 22, 2026
33f0e22
feat(foundations): align Button with Figma + add foundations spec
netchampfaris May 23, 2026
dbd2135
docs(button): replace prop stories with Figma examples + playground
netchampfaris May 23, 2026
368fada
feat(components): refine badge and button examples
netchampfaris May 23, 2026
1494a9e
feat(tokens): add elevation and focus-ring effect tokens
netchampfaris May 24, 2026
c771430
feat(icon): add shared Icon renderer
netchampfaris May 24, 2026
e84c73e
feat(pill): add Pill primitive
netchampfaris May 24, 2026
128851e
refactor(tab-buttons)!: render Pill, support route/href, drop Button …
netchampfaris May 24, 2026
7e4afe8
chore: regenerate components.d.ts and propsgen output
netchampfaris May 24, 2026
5390f85
refactor(docs): generic ComponentPlayground for component builders
netchampfaris May 24, 2026
2fa7dee
fix(pill): redesign vertical underline tab indicator
netchampfaris May 24, 2026
ae259c9
refactor(focus-ring): adopt focus-ring utilities across components
netchampfaris May 24, 2026
1d9b2a8
docs(tab-buttons): refresh story examples with realistic nav content
netchampfaris May 24, 2026
63a35ae
chore(tab-buttons): drop unused TabButtons.story.vue
netchampfaris May 24, 2026
8f2e637
docs(foundations): rename pages, refresh palette/semantic rendering
netchampfaris May 24, 2026
7d506d7
refactor(elevation): use one shadow set for both themes
netchampfaris May 24, 2026
6c3a6b7
fix(docs/playground): sync code snippet theme with dark mode
netchampfaris May 24, 2026
7163ec3
docs(foundations): refresh radius + typography token displays
netchampfaris May 25, 2026
048420c
chore(tokens): align tailwind/tokens.js exports with Figma-generated …
netchampfaris May 27, 2026
6117a2a
docs(playground): add interactive builders for 21 components
netchampfaris Jun 7, 2026
7d8f51e
docs(checkbox): add group, description, and setting-row story examples
netchampfaris Jun 7, 2026
3c743d6
fix(pill): punch active browser tab through the rail
netchampfaris Jun 7, 2026
a7da173
docs(site): use menu-bar surface for sidebar, formatting cleanups
netchampfaris Jun 7, 2026
d5e541e
fix: export Progress from package index
netchampfaris Jun 7, 2026
01b81f8
feat(tokens): adopt final espresso v2 Figma export
netchampfaris Jun 7, 2026
64d401b
refactor(tokens)!: migrate to espresso v2 token names
netchampfaris Jun 7, 2026
c591058
refactor(tokens)!: stop emitting legacy token aliases
netchampfaris Jun 7, 2026
0308c52
test: fix stale class assertions in Progress and Select specs
netchampfaris Jun 7, 2026
95b3bea
feat(focus-ring)!: apply the focus ring globally via :focus-visible o…
netchampfaris Jun 7, 2026
28c95ce
refactor(tokens): migrate main's new code to espresso v2 names
netchampfaris Jun 7, 2026
b8c232a
feat(tokens)!: source typography from text.styles with per-weight styles
netchampfaris Jun 8, 2026
b409d34
docs(foundations): polish foundation pages
netchampfaris Jun 8, 2026
bc9114c
refactor(tokens): migrate main's editor rewrite to espresso v2 names
netchampfaris Jun 8, 2026
3a23acf
fix(TabButtons): render native tabs as components, not tag strings
netchampfaris Jun 8, 2026
0f260fb
fix(Dialog): allow mouse focus of inputs inside the dialog
netchampfaris Jun 8, 2026
c542edb
fix(editor): stop clobbering ProseMirror's classes on EditorContent
netchampfaris Jun 9, 2026
11a1b9b
fix(editor): hide table toolbar on scroll
netchampfaris Jun 9, 2026
6ba2829
fix(editor): keep table resizing and scroll wrapper in read-only and …
netchampfaris Jun 9, 2026
4123381
fix(editor): center table toolbar over the visible part of a scrolled…
netchampfaris Jun 9, 2026
05a1319
fix(editor): default file uploads to private in TextEditor
netchampfaris Jun 10, 2026
e431f5e
chore(deps): upgrade tiptap and prosemirror
netchampfaris Jun 10, 2026
277e482
style: update surface elevation tokens for dialog and editor popups
netchampfaris Jun 10, 2026
e9c6f74
feat(editor): polish table interactions
netchampfaris Jun 10, 2026
34b88af
fix(tokens): include alpha semantic color tokens
netchampfaris Jun 10, 2026
bfe5518
fix(TabButtons): round focusable tab pills
netchampfaris Jun 10, 2026
2273a27
chore: remove temporary planning docs
netchampfaris Jun 10, 2026
808e1a0
fix(tokens): finish review follow-ups
netchampfaris Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
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
3 changes: 2 additions & 1 deletion components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +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']
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 @@ -109,6 +110,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 All @@ -129,7 +131,6 @@ declare module 'vue' {
SuggestionList: typeof import('./src/components/TextEditor/extensions/suggestion/SuggestionList.vue')['default']
Switch: typeof import('./src/components/Switch/Switch.vue')['default']
TabButtons: typeof import('./src/components/TabButtons/TabButtons.vue')['default']
'TabButtons.story': typeof import('./src/components/TabButtons/TabButtons.story.vue')['default']
Tabs: typeof import('./src/components/Tabs/Tabs.vue')['default']
Textarea: typeof import('./src/components/Textarea/Textarea.vue')['default']
TextEditor: typeof import('./src/components/TextEditor/TextEditor.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
58 changes: 54 additions & 4 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,67 @@
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 AlertBuilder from '../../components/Docs/AlertBuilder.vue'
import AvatarBuilder from '../../components/Docs/AvatarBuilder.vue'
import BreadcrumbsBuilder from '../../components/Docs/BreadcrumbsBuilder.vue'
import CheckboxBuilder from '../../components/Docs/CheckboxBuilder.vue'
import ComboboxBuilder from '../../components/Docs/ComboboxBuilder.vue'
import DialogBuilder from '../../components/Docs/DialogBuilder.vue'
import DividerBuilder from '../../components/Docs/DividerBuilder.vue'
import DropdownBuilder from '../../components/Docs/DropdownBuilder.vue'
import ErrorMessageBuilder from '../../components/Docs/ErrorMessageBuilder.vue'
import FormControlBuilder from '../../components/Docs/FormControlBuilder.vue'
import MultiSelectBuilder from '../../components/Docs/MultiSelectBuilder.vue'
import PasswordBuilder from '../../components/Docs/PasswordBuilder.vue'
import ProgressBuilder from '../../components/Docs/ProgressBuilder.vue'
import RatingBuilder from '../../components/Docs/RatingBuilder.vue'
import SelectBuilder from '../../components/Docs/SelectBuilder.vue'
import SliderBuilder from '../../components/Docs/SliderBuilder.vue'
import SwitchBuilder from '../../components/Docs/SwitchBuilder.vue'
import TabsBuilder from '../../components/Docs/TabsBuilder.vue'
import TextInputBuilder from '../../components/Docs/TextInputBuilder.vue'
import TextareaBuilder from '../../components/Docs/TextareaBuilder.vue'
import TooltipBuilder from '../../components/Docs/TooltipBuilder.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)
app.component('AlertBuilder', AlertBuilder)
app.component('AvatarBuilder', AvatarBuilder)
app.component('BreadcrumbsBuilder', BreadcrumbsBuilder)
app.component('CheckboxBuilder', CheckboxBuilder)
app.component('ComboboxBuilder', ComboboxBuilder)
app.component('DialogBuilder', DialogBuilder)
app.component('DividerBuilder', DividerBuilder)
app.component('DropdownBuilder', DropdownBuilder)
app.component('ErrorMessageBuilder', ErrorMessageBuilder)
app.component('FormControlBuilder', FormControlBuilder)
app.component('MultiSelectBuilder', MultiSelectBuilder)
app.component('PasswordBuilder', PasswordBuilder)
app.component('ProgressBuilder', ProgressBuilder)
app.component('RatingBuilder', RatingBuilder)
app.component('SelectBuilder', SelectBuilder)
app.component('SliderBuilder', SliderBuilder)
app.component('SwitchBuilder', SwitchBuilder)
app.component('TabsBuilder', TabsBuilder)
app.component('TextInputBuilder', TextInputBuilder)
app.component('TextareaBuilder', TextareaBuilder)
app.component('TooltipBuilder', TooltipBuilder)
},
} satisfies Theme
61 changes: 61 additions & 0 deletions docs/components/Docs/AlertBuilder.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<script setup lang="ts">
import { Alert } from 'frappe-ui'
import ComponentPlayground, { type Knob } from './ComponentPlayground.vue'

const knobs: Knob[] = [
{ name: 'title', type: 'text', default: 'Heads up', width: '14rem' },
{
name: 'description',
type: 'text',
default: 'You can dismiss this alert.',
width: '20rem',
},
{
name: 'theme',
type: 'tabs',
default: 'blue',
options: [
{ label: 'none', value: 'none' },
{ label: 'yellow', value: 'yellow' },
{ label: 'blue', value: 'blue' },
{ label: 'green', value: 'green' },
{ label: 'red', value: 'red' },
],
},
{
name: 'variant',
type: 'tabs',
default: 'subtle',
options: [
{ label: 'subtle', value: 'subtle' },
{ label: 'outline', value: 'outline' },
],
},
{ name: 'dismissible', type: 'switch', default: true },
]

function buildCode(v: Record<string, any>) {
const attrs = [`title="${v.title}"`]
if (v.description) attrs.push(`description="${v.description}"`)
if (v.theme !== 'none') attrs.push(`theme="${v.theme}"`)
if (v.variant !== 'subtle') attrs.push(`variant="${v.variant}"`)
if (!v.dismissible) attrs.push(':dismissible="false"')
return ['<Alert', ...attrs.map((a) => ' ' + a), '/>'].join('\n')
}
</script>

<template>
<ComponentPlayground :knobs="knobs" :code="buildCode" preview-min-height="120px">
<template #preview="{ values }">
<div class="w-full max-w-md">
<Alert
:title="values.title"
:description="values.description || undefined"
:theme="values.theme === 'none' ? undefined : values.theme"
:variant="values.variant"
:dismissible="values.dismissible"
/>
</div>
</template>
</ComponentPlayground>
</template>
53 changes: 53 additions & 0 deletions docs/components/Docs/AvatarBuilder.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<script setup lang="ts">
import { Avatar } from 'frappe-ui'
import ComponentPlayground, { type Knob } from './ComponentPlayground.vue'

const knobs: Knob[] = [
{ name: 'label', type: 'text', default: 'John Doe', width: '12rem' },
{ name: 'image', type: 'text', default: '', width: '20rem' },
{
name: 'size',
type: 'tabs',
default: 'md',
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' },
{ label: '3xl', value: '3xl' },
],
},
{
name: 'shape',
type: 'tabs',
default: 'circle',
options: [
{ label: 'circle', value: 'circle' },
{ label: 'square', value: 'square' },
],
},
]

function buildCode(v: Record<string, any>) {
const attrs = [`label="${v.label}"`]
if (v.image) attrs.push(`image="${v.image}"`)
if (v.size !== 'md') attrs.push(`size="${v.size}"`)
if (v.shape !== 'circle') attrs.push(`shape="${v.shape}"`)
return ['<Avatar', ...attrs.map((a) => ' ' + a), '/>'].join('\n')
}
</script>

<template>
<ComponentPlayground :knobs="knobs" :code="buildCode" preview-min-height="120px">
<template #preview="{ values }">
<Avatar
:label="values.label"
:image="values.image || undefined"
:size="values.size"
:shape="values.shape"
/>
</template>
</ComponentPlayground>
</template>
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>
48 changes: 48 additions & 0 deletions docs/components/Docs/BreadcrumbsBuilder.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<script setup lang="ts">
import { computed } from 'vue'
import { Breadcrumbs } from 'frappe-ui'
import ComponentPlayground, { type Knob } from './ComponentPlayground.vue'

const allItems = [
{ label: 'Workspace' },
{ label: 'Projects' },
{ label: 'Frappe UI' },
{ label: 'Pull Requests' },
{ label: '#716' },
]

const knobs: Knob[] = [
{
name: 'count',
type: 'tabs',
default: '3',
options: [
{ label: '2', value: '2' },
{ label: '3', value: '3' },
{ label: '4', value: '4' },
{ label: '5', value: '5' },
],
},
]

function itemsFor(count: number) {
return allItems.slice(0, count)
}

function buildCode(v: Record<string, any>) {
const count = Number(v.count)
const items = itemsFor(count)
const lines = items
.map((i) => ` { label: '${i.label}' },`)
.join('\n')
return `<Breadcrumbs :items="[\n${lines}\n ]" />`
}
</script>

<template>
<ComponentPlayground :knobs="knobs" :code="buildCode" preview-min-height="80px">
<template #preview="{ values }">
<Breadcrumbs :items="itemsFor(Number(values.count))" />
</template>
</ComponentPlayground>
</template>
Loading
Loading