@antfu/design 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +165 -0
- package/a11y/cli.ts +73 -0
- package/a11y/index.ts +13 -0
- package/a11y/scan.ts +127 -0
- package/components/Action/ActionButton.stories.ts +56 -0
- package/components/Action/ActionButton.vue +57 -0
- package/components/Action/ActionDarkToggle.stories.ts +31 -0
- package/components/Action/ActionDarkToggle.vue +87 -0
- package/components/Action/ActionIconButton.stories.ts +47 -0
- package/components/Action/ActionIconButton.vue +47 -0
- package/components/Display/DisplayAvatar.stories.ts +36 -0
- package/components/Display/DisplayAvatar.vue +58 -0
- package/components/Display/DisplayBadge.stories.ts +31 -0
- package/components/Display/DisplayBadge.vue +98 -0
- package/components/Display/DisplayBytes.stories.ts +28 -0
- package/components/Display/DisplayBytes.vue +30 -0
- package/components/Display/DisplayDate.stories.ts +37 -0
- package/components/Display/DisplayDate.vue +29 -0
- package/components/Display/DisplayDonut.stories.ts +26 -0
- package/components/Display/DisplayDonut.vue +46 -0
- package/components/Display/DisplayDuration.stories.ts +28 -0
- package/components/Display/DisplayDuration.vue +28 -0
- package/components/Display/DisplayFileIcon.stories.ts +27 -0
- package/components/Display/DisplayFileIcon.vue +30 -0
- package/components/Display/DisplayFilePath.stories.ts +30 -0
- package/components/Display/DisplayFilePath.vue +61 -0
- package/components/Display/DisplayKbd.stories.ts +26 -0
- package/components/Display/DisplayKbd.vue +27 -0
- package/components/Display/DisplayKeyValue.stories.ts +56 -0
- package/components/Display/DisplayKeyValue.vue +51 -0
- package/components/Display/DisplayLabel.stories.ts +27 -0
- package/components/Display/DisplayLabel.vue +33 -0
- package/components/Display/DisplayNumber.stories.ts +27 -0
- package/components/Display/DisplayNumber.vue +24 -0
- package/components/Display/DisplayNumberBadge.stories.ts +26 -0
- package/components/Display/DisplayNumberBadge.vue +22 -0
- package/components/Display/DisplayPackageName.stories.ts +26 -0
- package/components/Display/DisplayPackageName.vue +49 -0
- package/components/Display/DisplayProgressBar.stories.ts +29 -0
- package/components/Display/DisplayProgressBar.vue +90 -0
- package/components/Display/DisplayProportionBar.stories.ts +40 -0
- package/components/Display/DisplayProportionBar.vue +43 -0
- package/components/Display/DisplaySafeImage.stories.ts +43 -0
- package/components/Display/DisplaySafeImage.vue +30 -0
- package/components/Display/DisplayStatusPill.stories.ts +34 -0
- package/components/Display/DisplayStatusPill.vue +42 -0
- package/components/Display/DisplayTree.stories.ts +76 -0
- package/components/Display/DisplayTree.vue +102 -0
- package/components/Display/DisplayVersion.stories.ts +25 -0
- package/components/Display/DisplayVersion.vue +21 -0
- package/components/Feedback/FeedbackEmptyState.stories.ts +38 -0
- package/components/Feedback/FeedbackEmptyState.vue +21 -0
- package/components/Feedback/FeedbackLoading.stories.ts +23 -0
- package/components/Feedback/FeedbackLoading.vue +21 -0
- package/components/Feedback/FeedbackSpinner.stories.ts +25 -0
- package/components/Feedback/FeedbackSpinner.vue +22 -0
- package/components/Feedback/FeedbackTip.stories.ts +34 -0
- package/components/Feedback/FeedbackTip.vue +29 -0
- package/components/Feedback/FeedbackToasts.stories.ts +40 -0
- package/components/Feedback/FeedbackToasts.vue +105 -0
- package/components/Form/FormCheckbox.stories.ts +36 -0
- package/components/Form/FormCheckbox.vue +30 -0
- package/components/Form/FormCombobox.stories.ts +35 -0
- package/components/Form/FormCombobox.vue +83 -0
- package/components/Form/FormField.stories.ts +56 -0
- package/components/Form/FormField.vue +36 -0
- package/components/Form/FormNumberInput.stories.ts +47 -0
- package/components/Form/FormNumberInput.vue +85 -0
- package/components/Form/FormRadioGroup.stories.ts +47 -0
- package/components/Form/FormRadioGroup.vue +43 -0
- package/components/Form/FormSearchField.stories.ts +22 -0
- package/components/Form/FormSearchField.vue +32 -0
- package/components/Form/FormSelect.stories.ts +47 -0
- package/components/Form/FormSelect.vue +56 -0
- package/components/Form/FormSwitch.stories.ts +36 -0
- package/components/Form/FormSwitch.vue +26 -0
- package/components/Form/FormTextInput.stories.ts +39 -0
- package/components/Form/FormTextInput.vue +51 -0
- package/components/Form/FormTextarea.stories.ts +47 -0
- package/components/Form/FormTextarea.vue +32 -0
- package/components/Layout/LayoutBreadcrumb.stories.ts +54 -0
- package/components/Layout/LayoutBreadcrumb.vue +54 -0
- package/components/Layout/LayoutCard.stories.ts +31 -0
- package/components/Layout/LayoutCard.vue +21 -0
- package/components/Layout/LayoutDataTable.stories.ts +77 -0
- package/components/Layout/LayoutDataTable.vue +145 -0
- package/components/Layout/LayoutExpandableList.stories.ts +28 -0
- package/components/Layout/LayoutExpandableList.vue +94 -0
- package/components/Layout/LayoutPanelGrids.stories.ts +28 -0
- package/components/Layout/LayoutPanelGrids.vue +26 -0
- package/components/Layout/LayoutSectionBlock.stories.ts +37 -0
- package/components/Layout/LayoutSectionBlock.vue +37 -0
- package/components/Layout/LayoutSideNav.stories.ts +33 -0
- package/components/Layout/LayoutSideNav.vue +48 -0
- package/components/Layout/LayoutSplitPane.stories.ts +44 -0
- package/components/Layout/LayoutSplitPane.vue +30 -0
- package/components/Layout/LayoutTabs.stories.ts +43 -0
- package/components/Layout/LayoutTabs.vue +56 -0
- package/components/Layout/LayoutToolbar.stories.ts +60 -0
- package/components/Layout/LayoutToolbar.vue +28 -0
- package/components/Layout/LayoutVirtualList.stories.ts +30 -0
- package/components/Layout/LayoutVirtualList.vue +82 -0
- package/components/Overlay/OverlayDrawer.stories.ts +47 -0
- package/components/Overlay/OverlayDrawer.vue +58 -0
- package/components/Overlay/OverlayDropdown.stories.ts +25 -0
- package/components/Overlay/OverlayDropdown.vue +30 -0
- package/components/Overlay/OverlayDropdownItem.stories.ts +26 -0
- package/components/Overlay/OverlayDropdownItem.vue +31 -0
- package/components/Overlay/OverlayDropdownLabel.vue +9 -0
- package/components/Overlay/OverlayDropdownSeparator.vue +7 -0
- package/components/Overlay/OverlayModal.stories.ts +33 -0
- package/components/Overlay/OverlayModal.vue +48 -0
- package/components/Overlay/OverlayTooltip.stories.ts +33 -0
- package/components/Overlay/OverlayTooltip.vue +38 -0
- package/composables/colorScheme.ts +58 -0
- package/composables/toast.ts +81 -0
- package/package.json +99 -0
- package/skills/antfu-design/SKILL.md +65 -0
- package/skills/antfu-design/references/advanced-patterns.md +39 -0
- package/skills/antfu-design/references/best-practices.md +54 -0
- package/skills/antfu-design/references/core-components.md +72 -0
- package/skills/antfu-design/references/core-setup.md +56 -0
- package/skills/antfu-design/references/core-tokens.md +100 -0
- package/skills/antfu-design/references/features-data-presentation.md +27 -0
- package/splitpanes.d.ts +70 -0
- package/styles/animations.css +47 -0
- package/styles/base.css +31 -0
- package/styles/floating-vue.css +28 -0
- package/styles/index.css +7 -0
- package/styles/reka-ui.css +112 -0
- package/styles/scrollbar.css +24 -0
- package/styles/splitpanes.css +61 -0
- package/unocss/colors.ts +127 -0
- package/unocss/index.ts +99 -0
- package/unocss/options.ts +31 -0
- package/unocss/patterns.ts +38 -0
- package/unocss/rules.ts +26 -0
- package/unocss/severity.ts +16 -0
- package/unocss/shortcuts.ts +68 -0
- package/utils/color.ts +328 -0
- package/utils/contrast.ts +118 -0
- package/utils/format.ts +389 -0
- package/utils/icon.ts +200 -0
- package/utils/index.ts +13 -0
- package/utils/keybinding.ts +199 -0
- package/utils/misc.ts +141 -0
- package/utils/path.ts +243 -0
- package/utils/semver.ts +147 -0
- package/utils/tree.ts +89 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A controlled toast queue for {@link FeedbackToasts}.
|
|
3
|
+
*
|
|
4
|
+
* The package stays stateless — `FeedbackToasts` is presentational and the *app*
|
|
5
|
+
* owns the list. This composable is the ergonomic owner: create it once and share
|
|
6
|
+
* the returned `toasts` ref with the component, then `add`/`dismiss` from anywhere.
|
|
7
|
+
* Share a single instance app-wide via `provide`/`inject` or a module-level export.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const { toasts, add, dismiss } = useToast()
|
|
11
|
+
* add('Saved', { type: 'success', duration: 3000 })
|
|
12
|
+
* // <FeedbackToasts :items="toasts" @dismiss="dismiss" />
|
|
13
|
+
*/
|
|
14
|
+
import type { Ref } from 'vue'
|
|
15
|
+
import type { ToastItem, ToastType } from '../components/Feedback/FeedbackToasts.vue'
|
|
16
|
+
import { ref } from 'vue'
|
|
17
|
+
|
|
18
|
+
/** Options accepted when pushing a toast (everything but `id`/`message`). */
|
|
19
|
+
export type ToastInput = Omit<ToastItem, 'id' | 'message'> & { id?: ToastItem['id'] }
|
|
20
|
+
|
|
21
|
+
export interface ToastQueue {
|
|
22
|
+
/** The reactive toast list — pass to `<FeedbackToasts :items>`. */
|
|
23
|
+
toasts: Ref<ToastItem[]>
|
|
24
|
+
/** Push a toast; returns its id. Auto-dismisses after `duration` ms when set. */
|
|
25
|
+
add: (message: string, options?: ToastInput) => ToastItem['id']
|
|
26
|
+
/** Patch an existing toast (e.g. update `progress`). */
|
|
27
|
+
update: (id: ToastItem['id'], patch: Partial<ToastItem>) => void
|
|
28
|
+
/** Remove a toast by id. */
|
|
29
|
+
dismiss: (id: ToastItem['id']) => void
|
|
30
|
+
/** Remove all toasts. */
|
|
31
|
+
clear: () => void
|
|
32
|
+
/** Shorthands that preset `type`. */
|
|
33
|
+
success: (message: string, options?: ToastInput) => ToastItem['id']
|
|
34
|
+
error: (message: string, options?: ToastInput) => ToastItem['id']
|
|
35
|
+
info: (message: string, options?: ToastInput) => ToastItem['id']
|
|
36
|
+
warning: (message: string, options?: ToastInput) => ToastItem['id']
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let counter = 0
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Create a toast queue. Stateless package, caller owns the instance.
|
|
43
|
+
*
|
|
44
|
+
* @returns A {@link ToastQueue}.
|
|
45
|
+
*/
|
|
46
|
+
export function useToast(): ToastQueue {
|
|
47
|
+
const toasts = ref<ToastItem[]>([])
|
|
48
|
+
|
|
49
|
+
function dismiss(id: ToastItem['id']): void {
|
|
50
|
+
toasts.value = toasts.value.filter(t => t.id !== id)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function update(id: ToastItem['id'], patch: Partial<ToastItem>): void {
|
|
54
|
+
const item = toasts.value.find(t => t.id === id)
|
|
55
|
+
if (item)
|
|
56
|
+
Object.assign(item, patch)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function add(message: string, options: ToastInput = {}): ToastItem['id'] {
|
|
60
|
+
const { id = `t${++counter}`, duration, ...rest } = options
|
|
61
|
+
toasts.value.push({ id, message, ...rest })
|
|
62
|
+
if (duration != null && duration > 0)
|
|
63
|
+
setTimeout(dismiss, duration, id)
|
|
64
|
+
return id
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const withType = (type: ToastType) => (message: string, options: ToastInput = {}) =>
|
|
68
|
+
add(message, { type, ...options })
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
toasts,
|
|
72
|
+
add,
|
|
73
|
+
update,
|
|
74
|
+
dismiss,
|
|
75
|
+
clear: () => { toasts.value = [] },
|
|
76
|
+
success: withType('success'),
|
|
77
|
+
error: withType('error'),
|
|
78
|
+
info: withType('info'),
|
|
79
|
+
warning: withType('warning'),
|
|
80
|
+
}
|
|
81
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@antfu/design",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "A customizable, composable design system for devtools-style Vue apps — a UnoCSS preset, Vue primitives, a design skill, and an a11y contrast check",
|
|
6
|
+
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"funding": "https://github.com/sponsors/antfu",
|
|
9
|
+
"homepage": "https://github.com/antfu/design#readme",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/antfu/design.git",
|
|
13
|
+
"directory": "packages/design"
|
|
14
|
+
},
|
|
15
|
+
"bugs": "https://github.com/antfu/design/issues",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"unocss",
|
|
18
|
+
"unocss-preset",
|
|
19
|
+
"vue",
|
|
20
|
+
"design-system",
|
|
21
|
+
"components",
|
|
22
|
+
"devtools",
|
|
23
|
+
"antfu"
|
|
24
|
+
],
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"exports": {
|
|
27
|
+
"./unocss": "./unocss/index.ts",
|
|
28
|
+
"./utils": "./utils/index.ts",
|
|
29
|
+
"./composables/*": "./composables/*",
|
|
30
|
+
"./a11y": "./a11y/index.ts",
|
|
31
|
+
"./styles.css": "./styles/index.css",
|
|
32
|
+
"./styles/*": "./styles/*",
|
|
33
|
+
"./components/*": "./components/*",
|
|
34
|
+
"./splitpanes.d.ts": "./splitpanes.d.ts",
|
|
35
|
+
"./package.json": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"a11y",
|
|
39
|
+
"components",
|
|
40
|
+
"composables",
|
|
41
|
+
"skills",
|
|
42
|
+
"splitpanes.d.ts",
|
|
43
|
+
"styles",
|
|
44
|
+
"unocss",
|
|
45
|
+
"utils"
|
|
46
|
+
],
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"@axe-core/playwright": "^4.0.0",
|
|
49
|
+
"playwright": "^1.0.0",
|
|
50
|
+
"unocss": ">=66.0.0",
|
|
51
|
+
"vue": "^3.5.0"
|
|
52
|
+
},
|
|
53
|
+
"peerDependenciesMeta": {
|
|
54
|
+
"@axe-core/playwright": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"playwright": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"@antfu/utils": "^9.3.0",
|
|
63
|
+
"@iconify-json/catppuccin": "^1.2.17",
|
|
64
|
+
"@tanstack/vue-virtual": "^3.13.29",
|
|
65
|
+
"@unocss/core": "^66.7.2",
|
|
66
|
+
"@vueuse/core": "^14.3.0",
|
|
67
|
+
"colorjs.io": "^0.6.1",
|
|
68
|
+
"floating-vue": "^5.2.2",
|
|
69
|
+
"reka-ui": "^2.10.0",
|
|
70
|
+
"splitpanes": "^4.1.2"
|
|
71
|
+
},
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@arethetypeswrong/cli": "^0.18.2",
|
|
74
|
+
"@axe-core/playwright": "^4.12.1",
|
|
75
|
+
"@storybook/vue3-vite": "^10.4.6",
|
|
76
|
+
"@unocss/preset-icons": "^66.7.2",
|
|
77
|
+
"@unocss/preset-mini": "^66.7.2",
|
|
78
|
+
"@unocss/preset-web-fonts": "^66.7.2",
|
|
79
|
+
"@unocss/preset-wind3": "^66.7.2",
|
|
80
|
+
"@unocss/preset-wind4": "^66.7.2",
|
|
81
|
+
"@vitejs/plugin-vue": "^6.0.7",
|
|
82
|
+
"@vue/test-utils": "^2.4.11",
|
|
83
|
+
"happy-dom": "^20.10.6",
|
|
84
|
+
"playwright": "^1.61.1",
|
|
85
|
+
"tsdown": "^0.22.0",
|
|
86
|
+
"tsdown-stale-guard": "^0.1.2",
|
|
87
|
+
"tsnapi": "^0.3.3",
|
|
88
|
+
"unocss": "^66.7.2",
|
|
89
|
+
"unplugin-vue": "^7.2.0",
|
|
90
|
+
"vue": "^3.5.38",
|
|
91
|
+
"vue-tsc": "^3.3.5"
|
|
92
|
+
},
|
|
93
|
+
"scripts": {
|
|
94
|
+
"docs:gen": "tsx scripts/gen-tokens.ts",
|
|
95
|
+
"lint": "eslint",
|
|
96
|
+
"typecheck": "vue-tsc --noEmit",
|
|
97
|
+
"test": "vitest run"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: antfu-design
|
|
3
|
+
description: Use when building or restyling devtools-style Vue 3 UIs with @antfu/design — wiring the composable UnoCSS preset, using the semantic token vocabulary (bg-base, color-base, badge-color-*, color-scale-*), choosing prefixed primitives (DisplayBadge, ActionButton, DisplayFilePath, OverlayModal, …), and keeping light/dark contrast and the "anti-slop" rules. Reach for it whenever generating or reviewing UI in a project that depends on @antfu/design.
|
|
4
|
+
metadata:
|
|
5
|
+
author: antfu
|
|
6
|
+
version: 2026.06.26
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Doing good design with `@antfu/design`
|
|
10
|
+
|
|
11
|
+
`@antfu/design` is a shared design layer for devtools-style Vue apps: a
|
|
12
|
+
**composable UnoCSS preset** (`presetAnthonyDesign`), a set of **token-driven Vue
|
|
13
|
+
primitives**, and a **color-contrast a11y check**. This skill is how to use it
|
|
14
|
+
*well* — the vocabulary, the defaults, and the taste.
|
|
15
|
+
|
|
16
|
+
## The three rules that matter most
|
|
17
|
+
|
|
18
|
+
1. **Own the tokens, not the colors.** Never hard-code a hex or a raw Tailwind
|
|
19
|
+
color in app UI. Reach for the semantic layer first: `bg-base`, `color-base`,
|
|
20
|
+
`color-muted`, `border-base`, `op-fade`, `btn-action`, `badge`. They are
|
|
21
|
+
defined once (in the preset) and adapt to light/dark automatically. See
|
|
22
|
+
[references/core-tokens.md](references/core-tokens.md).
|
|
23
|
+
|
|
24
|
+
2. **Light/dark parity is not optional.** Every surface and text token has a dark
|
|
25
|
+
variant baked in. If you write a one-off color, you have just created a
|
|
26
|
+
dark-mode bug. The standing contrast scan (`@antfu/design/a11y`) will catch the
|
|
27
|
+
worst of it — don't rely on it as a substitute for using the tokens.
|
|
28
|
+
|
|
29
|
+
3. **No slop.** Technical values are `font-mono tabular-nums`. No em-dash–laden
|
|
30
|
+
prose in UI copy (the **dash ban**). Prefer one obvious affordance over three
|
|
31
|
+
competing ones. See [references/best-practices.md](references/best-practices.md).
|
|
32
|
+
|
|
33
|
+
## When to reach for what
|
|
34
|
+
|
|
35
|
+
- Showing a count, size, duration, or date → a **display component**
|
|
36
|
+
(`DisplayNumber`, `DisplayBytes`, `DisplayDuration`, `DisplayDate`), not raw text.
|
|
37
|
+
- A status, type, or tag → `DisplayBadge` (hash- or palette-colored) or `DisplayLabel`.
|
|
38
|
+
- A file/module path → `DisplayFilePath` (truncates, dims directories, links).
|
|
39
|
+
- An overlay → `OverlayModal` / `OverlayDrawer` (reka-ui), a popover → `OverlayTooltip` / `OverlayDropdown`.
|
|
40
|
+
- A severity (fresh→stale, fast→slow, small→large) → the `color-scale-*` ramp via
|
|
41
|
+
the display components' `colorize` prop, never ad-hoc red/green.
|
|
42
|
+
|
|
43
|
+
Components are categorized and prefixed by category (`Display*`, `Form*`,
|
|
44
|
+
`Overlay*`, `Layout*`, `Action*`, `Feedback*`). Dark mode is the app's to own —
|
|
45
|
+
the package ships no `isDark`/`toggleDark`; components that vary by scheme take a
|
|
46
|
+
`colorScheme: 'light' | 'dark'` prop, and `ActionDarkToggle` is controlled.
|
|
47
|
+
|
|
48
|
+
Full catalog: [references/core-components.md](references/core-components.md).
|
|
49
|
+
|
|
50
|
+
## Setup
|
|
51
|
+
|
|
52
|
+
It's a **single preset, not self-contained** — it bundles no base preset,
|
|
53
|
+
icons, fonts, or reset. You add those. See
|
|
54
|
+
[references/core-setup.md](references/core-setup.md) for wiring the preset,
|
|
55
|
+
importing styles, and pointing UnoCSS at the package so the components' classes
|
|
56
|
+
get generated.
|
|
57
|
+
|
|
58
|
+
## References
|
|
59
|
+
|
|
60
|
+
- [core-setup.md](references/core-setup.md) — install + wire the preset, import styles.
|
|
61
|
+
- [core-tokens.md](references/core-tokens.md) — the canonical token table (generated from the preset).
|
|
62
|
+
- [core-components.md](references/core-components.md) — the component catalog with import paths.
|
|
63
|
+
- [best-practices.md](references/best-practices.md) — class-over-attributify, parity, mono values, the dash ban, the "three dials".
|
|
64
|
+
- [features-data-presentation.md](references/features-data-presentation.md) — presenting numbers, sizes, durations, paths.
|
|
65
|
+
- [advanced-patterns.md](references/advanced-patterns.md) — composition patterns and a redesign protocol.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Advanced patterns
|
|
2
|
+
|
|
3
|
+
## Composition over configuration
|
|
4
|
+
|
|
5
|
+
The primitives are deliberately thin so you compose them into app-local
|
|
6
|
+
patterns rather than reaching for a mega-component:
|
|
7
|
+
|
|
8
|
+
- **Stat row** — a label + a mono value + an optional `DisplayBadge`:
|
|
9
|
+
`color-muted` label, `DisplayNumber`/`DisplayBytes` value.
|
|
10
|
+
- **Toolbar** — a glass sticky bar (`bg-glass z-nav`) with a `FormSearchField` and
|
|
11
|
+
`ActionIconButton`s.
|
|
12
|
+
- **Panel** — `LayoutCard` + `LayoutSectionBlock`s; `FeedbackEmptyState` when there's nothing.
|
|
13
|
+
- **Detail list** — `LayoutVirtualList` of rows, each a `DisplayFilePath`/`DisplayPackageName` +
|
|
14
|
+
display badges, colorized by one severity dimension.
|
|
15
|
+
|
|
16
|
+
When such a composition recurs across screens, lift it into a local component —
|
|
17
|
+
not into the design system (the system stays primitive-focused).
|
|
18
|
+
|
|
19
|
+
## Overriding a primitive
|
|
20
|
+
|
|
21
|
+
Because components are thin and token-driven, overriding is usually a prop or a
|
|
22
|
+
token, not a fork. When you genuinely need to fork one, copy the single readable
|
|
23
|
+
file from `@antfu/design/components/<Name>.vue` — that subpath exists for exactly
|
|
24
|
+
this. Keep using the same tokens so it stays on-theme.
|
|
25
|
+
|
|
26
|
+
## Redesign protocol
|
|
27
|
+
|
|
28
|
+
When restyling an existing screen onto `@antfu/design`:
|
|
29
|
+
|
|
30
|
+
1. **Map colors to tokens first.** Replace every raw color with a semantic token
|
|
31
|
+
(`bg-base`, `color-muted`, `border-base`, `color-scale-*`). Resist new colors.
|
|
32
|
+
2. **Swap raw elements for primitives** one family at a time (badges, then
|
|
33
|
+
buttons, then inputs…), checking the visual diff after each.
|
|
34
|
+
3. **Run the contrast scan** (`@antfu/design/a11y`, or `tsx …/a11y/cli.ts <url>`) in light *and* dark.
|
|
35
|
+
4. **Tune the three dials** (density, hierarchy, affordance) — see best-practices.
|
|
36
|
+
|
|
37
|
+
The goal of a migration is near-zero visual diff: you are unifying what exists,
|
|
38
|
+
not redesigning it. Per-project theme config (primary color, fonts, base) is
|
|
39
|
+
expected, not a regression.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Best practices
|
|
2
|
+
|
|
3
|
+
Preserved from the original antfu design philosophy, reframed around the package.
|
|
4
|
+
|
|
5
|
+
## Class over attributify
|
|
6
|
+
|
|
7
|
+
Write utilities as **classes**, not attributes. Classes are greppable, sort
|
|
8
|
+
cleanly (the UnoCSS ESLint plugin orders them), and read consistently.
|
|
9
|
+
|
|
10
|
+
```vue
|
|
11
|
+
<!-- good -->
|
|
12
|
+
<div class="flex items-center gap-2 color-muted">
|
|
13
|
+
<!-- avoid -->
|
|
14
|
+
<div flex items-center gap-2 color-muted>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Light/dark parity
|
|
18
|
+
|
|
19
|
+
Never write a one-off color. Every surface/text/border has a token with a dark
|
|
20
|
+
variant baked in (`bg-base`, `color-base`, `border-base`, …). A raw `bg-white`
|
|
21
|
+
or `text-gray-800` is a dark-mode bug waiting to happen. When you truly need a
|
|
22
|
+
custom color, define it through the theme (a `primary`/`warning`/… ramp) so both
|
|
23
|
+
modes are covered.
|
|
24
|
+
|
|
25
|
+
## Mono + tabular for technical values
|
|
26
|
+
|
|
27
|
+
Numbers, sizes, durations, versions, hashes, paths → `font-mono tabular-nums`
|
|
28
|
+
so columns align and digits don't jitter. Prefer the display components
|
|
29
|
+
(`DisplayNumber`, `DisplayBytes`, `DisplayDuration`, `DisplayVersion`) which already do
|
|
30
|
+
this.
|
|
31
|
+
|
|
32
|
+
## Anti-slop: the dash ban
|
|
33
|
+
|
|
34
|
+
No em-dash–driven prose in UI copy or generated text. Keep microcopy short and
|
|
35
|
+
declarative. Don't pad labels ("Click here to view the…") — name the thing.
|
|
36
|
+
|
|
37
|
+
## The three dials
|
|
38
|
+
|
|
39
|
+
When reviewing a screen, tune in this order:
|
|
40
|
+
|
|
41
|
+
1. **Density** — how much breathing room (`gap`, `p`, line-height). Devtools UIs
|
|
42
|
+
skew dense; be deliberate, not cramped.
|
|
43
|
+
2. **Hierarchy** — `color-base` vs `color-muted` vs `color-faint`, weight, size.
|
|
44
|
+
Most text is muted; reserve `color-base` for what matters and `color-active`
|
|
45
|
+
for the one accent.
|
|
46
|
+
3. **Affordance** — one obvious action per context. Prefer a single
|
|
47
|
+
`btn-primary` over three competing buttons. Severity via `color-scale-*`, not
|
|
48
|
+
ad-hoc red/green.
|
|
49
|
+
|
|
50
|
+
## Severity, not vibes
|
|
51
|
+
|
|
52
|
+
Fresh→stale, fast→slow, small→large all map to the same five-stop ramp
|
|
53
|
+
(`color-scale-neutral|low|medium|high|critical`). Use the `colorize` prop on the
|
|
54
|
+
display components instead of hand-picking colors per case.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Component catalog
|
|
2
|
+
|
|
3
|
+
All from `@antfu/design`, explicit imports, token-driven, dark-aware. Components
|
|
4
|
+
are grouped into categories and **prefixed with their category** (e.g.
|
|
5
|
+
`DisplayBadge`, `FormTextInput`, `OverlayModal`) — the import path mirrors this
|
|
6
|
+
(`@antfu/design/components/Display/DisplayBadge.vue`). Overlay behavior comes
|
|
7
|
+
from reka-ui; tooltips/poppers from floating-vue; resizable panes from
|
|
8
|
+
splitpanes — all themed through the shipped CSS overrides.
|
|
9
|
+
|
|
10
|
+
> **Dark mode is the app's to own.** The package does not ship `isDark`/`toggleDark`.
|
|
11
|
+
> Components that vary by scheme (e.g. hash-colored `DisplayBadge`, `DisplayLabel`,
|
|
12
|
+
> `DisplayPackageName`, `DisplayProportionBar`) take a `colorScheme: 'light' | 'dark'`
|
|
13
|
+
> prop. `ActionDarkToggle` is **controlled**: `colorScheme` prop + `update:colorScheme`.
|
|
14
|
+
|
|
15
|
+
## Action
|
|
16
|
+
|
|
17
|
+
| Component | Use it for |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `ActionButton` | actions. `variant` action/primary/text, polymorphic `href`/`as`, `icon`, `loading`. |
|
|
20
|
+
| `ActionIconButton` | a round icon-only button with a `tooltip`, `active` state, `#badge`. |
|
|
21
|
+
| `ActionDarkToggle` | controlled dark toggle (`v-model:colorScheme`) with a view-transition reveal. |
|
|
22
|
+
|
|
23
|
+
## Display
|
|
24
|
+
|
|
25
|
+
| Component | Use it for |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `DisplayBadge` | a status/type/tag chip — hash-colored from text, a palette name (`color="green"`), a hue, or muted. `colorScheme`, `variant`, `size`, `icon`. |
|
|
28
|
+
| `DisplayNumber` / `DisplayNumberBadge` | formatted numbers (`Intl`), mono + tabular, `prefix`/`suffix`. |
|
|
29
|
+
| `DisplayDuration` | ms → human, `colorize` by severity. |
|
|
30
|
+
| `DisplayBytes` | humanized size, `colorize`, percent of `total`. |
|
|
31
|
+
| `DisplayDate` | relative time + exact-date tooltip, `colorize` by age, `live`. |
|
|
32
|
+
| `DisplayVersion` | `vX.Y.Z` prefix logic. |
|
|
33
|
+
| `DisplayPackageName` | scope colored by hash (`colorScheme`). |
|
|
34
|
+
| `DisplayFilePath` | truncates, dims directories, decodes `.pnpm`, icon, link. |
|
|
35
|
+
| `DisplayFileIcon` | ext → icon via a configurable rule list. |
|
|
36
|
+
| `DisplayLabel` | hex → contrast-aware tinted chip (`colorScheme`). |
|
|
37
|
+
| `DisplayStatusPill` | severity dot + label, `pulse`. |
|
|
38
|
+
| `DisplayDonut` / `DisplayProportionBar` | progress ring / stacked proportion bar. |
|
|
39
|
+
| `DisplayKbd` | renders `mod+k` as platform glyphs. |
|
|
40
|
+
|
|
41
|
+
## Form
|
|
42
|
+
|
|
43
|
+
`FormTextInput`, `FormSearchField` (icon + `DisplayKbd` hint + clear),
|
|
44
|
+
`FormCheckbox`, `FormSwitch`, `FormRadioGroup`, `FormSelect`.
|
|
45
|
+
|
|
46
|
+
## Overlay
|
|
47
|
+
|
|
48
|
+
`OverlayModal`, `OverlayDrawer`, `OverlayDropdown` (+ `OverlayDropdownItem`),
|
|
49
|
+
`OverlayTooltip`.
|
|
50
|
+
|
|
51
|
+
## Layout
|
|
52
|
+
|
|
53
|
+
`LayoutCard`, `LayoutSectionBlock` (collapsible), `LayoutSplitPane` (+ `Pane`),
|
|
54
|
+
`LayoutTabs` (underline/segment, `count` chips), `LayoutVirtualList`
|
|
55
|
+
(`@tanstack/vue-virtual`), `LayoutExpandableList` ("show N more").
|
|
56
|
+
|
|
57
|
+
## Feedback
|
|
58
|
+
|
|
59
|
+
`FeedbackSpinner`, `FeedbackLoading`, `FeedbackEmptyState`, `FeedbackTip`
|
|
60
|
+
(info/success/warning/error), `FeedbackToastProvider` (+ `useNotification()`).
|
|
61
|
+
|
|
62
|
+
## Composables & utils
|
|
63
|
+
|
|
64
|
+
- `@antfu/design/composables` — `useInputFocus`, `provideNotification` / `useNotification`.
|
|
65
|
+
(Dark mode, clipboard and persisted state are **not** wrapped — use the app's own
|
|
66
|
+
state and VueUse `useClipboard` / `useLocalStorage` directly.)
|
|
67
|
+
- `@antfu/design/utils` — `getHashColorFromString`, `getPluginColor`, `labelStyle`
|
|
68
|
+
(all take an explicit dark flag), `formatBytes`/`formatDuration`/`formatTimeAgo`
|
|
69
|
+
(+ severity colors), `parseReadablePath`, `parseSemverRange`, `toTree`,
|
|
70
|
+
`parseChord`/`bindingDisplay`, WCAG `contrastRatio`.
|
|
71
|
+
|
|
72
|
+
> Out this iteration: `CodeBlock` / `DiffView` and the command palette (deferred).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Setup
|
|
2
|
+
|
|
3
|
+
Install the package and a UnoCSS base. It's a **single preset, not
|
|
4
|
+
self-contained** — it bundles no base preset, icons, fonts, or reset.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pnpm add @antfu/design unocss
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Wiring the preset
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
// uno.config.ts
|
|
14
|
+
import { presetAnthonyDesign } from '@antfu/design/unocss'
|
|
15
|
+
import { defineConfig, presetIcons, presetWebFonts, presetWind4 } from 'unocss'
|
|
16
|
+
|
|
17
|
+
export default defineConfig({
|
|
18
|
+
presets: [
|
|
19
|
+
presetAnthonyDesign({
|
|
20
|
+
primary: '#49833E', // string | full color-scale object (default antfu green)
|
|
21
|
+
darkBackground: '#111', // near-black for dark surfaces
|
|
22
|
+
}),
|
|
23
|
+
presetWind4(), // a base preset is REQUIRED — shortcuts expand into its utilities
|
|
24
|
+
presetIcons(),
|
|
25
|
+
presetWebFonts({ fonts: { sans: 'DM Sans', mono: 'DM Mono' } }),
|
|
26
|
+
],
|
|
27
|
+
// Generate the components' classes by scanning the installed package:
|
|
28
|
+
content: { pipeline: { include: [/@antfu\/design/] } },
|
|
29
|
+
})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
> The base preset is **yours** to choose (`presetWind4`, `presetWind3` or
|
|
33
|
+
> `presetMini`). Without one, the semantic shortcuts have nothing to expand into.
|
|
34
|
+
> The design layer is **one preset** — there are no sub-presets to compose.
|
|
35
|
+
|
|
36
|
+
## Styles
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import '@antfu/design/styles.css' // everything
|
|
40
|
+
// …or cherry-pick:
|
|
41
|
+
import '@antfu/design/styles/floating-vue.css'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Add a reset yourself (`@unocss/reset`) — the design system does not bundle one.
|
|
45
|
+
|
|
46
|
+
## Components
|
|
47
|
+
|
|
48
|
+
Imported by **full path** (no barrel), categorized and category-prefixed:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import ActionButton from '@antfu/design/components/Action/ActionButton.vue'
|
|
52
|
+
import DisplayBadge from '@antfu/design/components/Display/DisplayBadge.vue'
|
|
53
|
+
import OverlayModal from '@antfu/design/components/Overlay/OverlayModal.vue'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The package ships raw `.ts` / `.vue` — your build compiles it. No auto-import resolver.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Token reference
|
|
2
|
+
|
|
3
|
+
This is the **canonical token vocabulary** — the contract between the preset,
|
|
4
|
+
the components, and your app code. Use these names verbatim; do not hand-roll
|
|
5
|
+
equivalents.
|
|
6
|
+
|
|
7
|
+
The table below is **generated from the preset** (`pnpm docs:gen`), so it can
|
|
8
|
+
never drift from what the shortcuts actually resolve to.
|
|
9
|
+
|
|
10
|
+
<!-- TOKENS:START -->
|
|
11
|
+
### Semantic & composite shortcuts
|
|
12
|
+
|
|
13
|
+
| Token | Expands to |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `color-base` | `color-neutral-800 dark:color-neutral-200` |
|
|
16
|
+
| `color-muted` | `color-neutral-600 dark:color-neutral-400` |
|
|
17
|
+
| `color-faint` | `color-neutral-500 dark:color-neutral-500` |
|
|
18
|
+
| `color-active` | `color-primary-600 dark:color-primary-300` |
|
|
19
|
+
| `bg-base` | `bg-white dark:bg-#111` |
|
|
20
|
+
| `bg-secondary` | `bg-#f5f5f5 dark:bg-#1a1a1a` |
|
|
21
|
+
| `bg-active` | `bg-#8881` |
|
|
22
|
+
| `bg-hover` | `bg-primary/5` |
|
|
23
|
+
| `bg-code` | `bg-gray-500/5` |
|
|
24
|
+
| `bg-tooltip` | `bg-white/75 dark:bg-#111/75 backdrop-blur-8` |
|
|
25
|
+
| `bg-gradient-more` | `bg-gradient-to-t from-white via-white/80 to-white/0 dark:from-#111 dark:via-#111/80 dark:to-#111/0` |
|
|
26
|
+
| `border-base` | `border-#8882` |
|
|
27
|
+
| `border-mute` | `border-#8881` |
|
|
28
|
+
| `border-active` | `border-primary-600/25 dark:border-primary-400/25` |
|
|
29
|
+
| `ring-base` | `ring-#8882` |
|
|
30
|
+
| `op-fade` | `op65 dark:op55` |
|
|
31
|
+
| `op-mute` | `op30 dark:op25` |
|
|
32
|
+
| `btn-action` | `border border-base rounded flex gap-2 items-center px2 py1 op75 hover:op100 hover:bg-active transition disabled:pointer-events-none disabled:op30! outline-none focus-visible:ring-2 focus-visible:ring-primary-500/40` |
|
|
33
|
+
| `btn-action-sm` | `btn-action text-sm` |
|
|
34
|
+
| `btn-action-active` | `color-active border-active! bg-active op100!` |
|
|
35
|
+
| `btn-icon` | `w-9 h-9 rounded-full op-fade hover:op100 hover:bg-active transition flex items-center justify-center disabled:pointer-events-none disabled:op30 outline-none focus-visible:ring-2 focus-visible:ring-primary-500/40` |
|
|
36
|
+
| `btn-icon-compact` | `w-6 h-6 rounded op-fade hover:op100 hover:bg-active transition flex items-center justify-center disabled:pointer-events-none disabled:op30 outline-none focus-visible:ring-2 focus-visible:ring-primary-500/40` |
|
|
37
|
+
| `btn-primary` | `px3 py1.5 rounded flex gap-2 items-center bg-primary-500 hover:bg-primary-600 text-white transition disabled:op50 disabled:pointer-events-none outline-none focus-visible:ring-2 focus-visible:ring-primary-500/40` |
|
|
38
|
+
| `badge` | `inline-flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium leading-none` |
|
|
39
|
+
| `badge-active` | `badge bg-active color-active` |
|
|
40
|
+
| `badge-muted` | `badge bg-#8881 color-muted` |
|
|
41
|
+
|
|
42
|
+
### Severity scale
|
|
43
|
+
|
|
44
|
+
| Token | Expands to |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `color-scale-neutral` | `text-gray-700 dark:text-gray-300` |
|
|
47
|
+
| `color-scale-low` | `text-lime-700 dark:text-lime-300 dark:saturate-75` |
|
|
48
|
+
| `color-scale-medium` | `text-amber-700 dark:text-amber-300 dark:saturate-90` |
|
|
49
|
+
| `color-scale-high` | `text-orange-700 dark:text-orange-300` |
|
|
50
|
+
| `color-scale-critical` | `text-red-700 dark:text-red-300` |
|
|
51
|
+
|
|
52
|
+
### Type sizes
|
|
53
|
+
|
|
54
|
+
| Token | Expands to |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `text-micro` | `text-[10px] leading-[1.4]` |
|
|
57
|
+
| `text-mini` | `text-[11px] leading-[1.45]` |
|
|
58
|
+
| `text-compact` | `text-[12px] leading-[1.5]` |
|
|
59
|
+
|
|
60
|
+
### Named z-index layers
|
|
61
|
+
|
|
62
|
+
| Token | Expands to |
|
|
63
|
+
|---|---|
|
|
64
|
+
| `z-nav` | `z-[30]` |
|
|
65
|
+
| `z-dropdown` | `z-[40]` |
|
|
66
|
+
| `z-tooltip` | `z-[45]` |
|
|
67
|
+
| `z-toast` | `z-[50]` |
|
|
68
|
+
| `z-modal-backdrop` | `z-[60]` |
|
|
69
|
+
| `z-modal-content` | `z-[70]` |
|
|
70
|
+
| `z-drawer-backdrop` | `z-[80]` |
|
|
71
|
+
| `z-drawer-content` | `z-[90]` |
|
|
72
|
+
|
|
73
|
+
### Dynamic
|
|
74
|
+
|
|
75
|
+
| Token | Expands to |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `badge-color-<name>` | a chip tinted by any palette color name (dark-aware) |
|
|
78
|
+
| `bg-glass` / `bg-glass:<n>` | translucent surface + `backdrop-blur` |
|
|
79
|
+
| `bg-dots` / `bg-dots-<n>` | radial dot-grid background, variable cell size in px (default 16) |
|
|
80
|
+
| `bg-grid` / `bg-grid-<n>` | crosshatch grid-lines background, variable cell size in px (default 16) |
|
|
81
|
+
<!-- TOKENS:END -->
|
|
82
|
+
|
|
83
|
+
## How to read it
|
|
84
|
+
|
|
85
|
+
- **Semantic shortcuts** (`bg-base`, `color-muted`, `border-base`, `op-fade`, …)
|
|
86
|
+
are the everyday vocabulary. They expand to the listed utilities and carry a
|
|
87
|
+
dark variant.
|
|
88
|
+
- **Composite shortcuts** (`btn-action`, `btn-primary`, `badge`) expand to a full
|
|
89
|
+
recipe — use them as-is.
|
|
90
|
+
- **Dynamic** `badge-color-<name>` tints a chip by any palette color name; `bg-glass`
|
|
91
|
+
/ `bg-glass:<n>` makes a translucent blurred surface.
|
|
92
|
+
- **Severity** `color-scale-{neutral,low,medium,high,critical}` is the one ramp
|
|
93
|
+
for fresh→stale / fast→slow / small→large. Prefer the `colorize` prop on display
|
|
94
|
+
components over using these directly.
|
|
95
|
+
- **Named z-index layers** (`z-nav` < `z-dropdown` < `z-tooltip` < `z-toast` <
|
|
96
|
+
`z-modal-backdrop` < `z-modal-content` < `z-drawer-backdrop` < `z-drawer-content`)
|
|
97
|
+
— never raw `z-<n>`.
|
|
98
|
+
- **Theme**: `font-sans` = DM Sans, `font-mono` = DM Mono; extra sizes
|
|
99
|
+
`text-micro` / `text-mini` / `text-compact`; color ramps `primary` (default
|
|
100
|
+
antfu green), `warning`, `success`, `error`.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Presenting data
|
|
2
|
+
|
|
3
|
+
Devtools UIs are mostly *data*. Reach for a display component before raw text —
|
|
4
|
+
they encode the formatting + severity decisions consistently.
|
|
5
|
+
|
|
6
|
+
| You have… | Use | Notes |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| a count / quantity | `DisplayNumber` / `DisplayNumberBadge` | `Intl` formatting, mono + tabular, prefix/suffix |
|
|
9
|
+
| a byte size | `DisplayBytes` | `base` 1024/1000, `colorize`, percent of `total` |
|
|
10
|
+
| an elapsed time | `DisplayDuration` | ms → human, `colorize` (fast=neutral … slow=critical) |
|
|
11
|
+
| a timestamp | `DisplayDate` | relative + exact tooltip, `colorize` by age, `live` |
|
|
12
|
+
| a module / file path | `DisplayFilePath` | truncates, dims directories, decodes `.pnpm` → `~`, icon |
|
|
13
|
+
| a package name | `DisplayPackageName` | scope colored by hash |
|
|
14
|
+
| a version | `DisplayVersion` | `vX.Y.Z` prefix logic |
|
|
15
|
+
| a proportion | `DisplayProportionBar` / `DisplayDonut` | stacked % bar / progress ring |
|
|
16
|
+
| a status / health | `DisplayStatusPill` | severity dot + label, `pulse` |
|
|
17
|
+
| a long list | `LayoutVirtualList` / `LayoutExpandableList` | virtualize, or "show N more" with a fade |
|
|
18
|
+
|
|
19
|
+
## Rules of thumb
|
|
20
|
+
|
|
21
|
+
- **Colorize by meaning, once.** Turn on `colorize` for the one dimension that
|
|
22
|
+
carries signal (e.g. staleness in a list of dates); don't rainbow everything.
|
|
23
|
+
- **Right-align numeric columns** and keep them `tabular-nums` so they scan.
|
|
24
|
+
- **Truncate paths, never wrap them.** `DisplayFilePath` keeps the basename legible and
|
|
25
|
+
exposes the full path on hover.
|
|
26
|
+
- **Hash colors for identity, palette colors for category.** A package name gets
|
|
27
|
+
a stable hash color; a known type (esm/cjs) gets a fixed `badge-color-*`.
|
package/splitpanes.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in fallback types for `splitpanes` — for consumers whose installed
|
|
3
|
+
* `splitpanes` predates bundled TypeScript types (v4.1.2+ ships its own, so you
|
|
4
|
+
* usually don't need this).
|
|
5
|
+
*
|
|
6
|
+
* It is deliberately NOT part of this package's own `tsconfig` (which would
|
|
7
|
+
* duplicate-declare against the real types). Consumers on an older splitpanes
|
|
8
|
+
* can opt in by adding to a `.d.ts` in their project:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* /// <reference types="@antfu/design/splitpanes.d.ts" />
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
declare module 'splitpanes' {
|
|
15
|
+
import type { DefineComponent } from 'vue'
|
|
16
|
+
|
|
17
|
+
export interface PaneData {
|
|
18
|
+
min: number
|
|
19
|
+
max: number
|
|
20
|
+
size: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface Pane {
|
|
24
|
+
id: number
|
|
25
|
+
el: HTMLElement | null
|
|
26
|
+
min: number
|
|
27
|
+
max: number
|
|
28
|
+
givenSize: number | null
|
|
29
|
+
size: number
|
|
30
|
+
index: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface SplitpanesReadyPayload {
|
|
34
|
+
panes: PaneData[]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface SplitpanesResizePayload {
|
|
38
|
+
event: MouseEvent | TouchEvent
|
|
39
|
+
index: number
|
|
40
|
+
prevPane: Pane | undefined
|
|
41
|
+
nextPane: Pane | undefined
|
|
42
|
+
panes: PaneData[]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** `resized` fires on splitter drag-end AND after pane add/remove (latter omit event/index). */
|
|
46
|
+
export interface SplitpanesResizedPayload {
|
|
47
|
+
event?: MouseEvent | TouchEvent
|
|
48
|
+
index?: number
|
|
49
|
+
prevPane?: Pane
|
|
50
|
+
nextPane?: Pane
|
|
51
|
+
panes: PaneData[]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface SplitpanesProps {
|
|
55
|
+
horizontal?: boolean
|
|
56
|
+
pushOtherPanes?: boolean
|
|
57
|
+
maximizePanes?: boolean
|
|
58
|
+
rtl?: boolean
|
|
59
|
+
firstSplitter?: boolean
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface PaneProps {
|
|
63
|
+
size?: number | string
|
|
64
|
+
minSize?: number | string
|
|
65
|
+
maxSize?: number | string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const Splitpanes: DefineComponent<SplitpanesProps>
|
|
69
|
+
export const Pane: DefineComponent<PaneProps>
|
|
70
|
+
}
|