@juxtui/ui 0.3.0 → 0.4.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/README.md CHANGED
@@ -1,62 +1,63 @@
1
- # juxt
2
-
3
- **Thoughtfully crafted components for Nuxt & Vue.**
4
-
5
- Juxt is a small component library that cares about the details you feel but rarely notice: how far a menu travels when it opens, where focus goes when a dialog closes, whether a loading button changes width, what happens when you press Escape with a select open inside a dialog.
6
-
7
- It is Multiple components, one token system and one motion language. It's mostly grayscale, and the green `#079D6B` is kept for state.
8
-
9
- ```vue
10
- <JButton>
11
- Save changes
12
- </JButton>
13
- ```
14
-
15
- ## Installation
16
-
17
- ```bash
18
- pnpm add @juxtui/ui
19
- ```
20
-
21
- **Nuxt**: add the module. Components, composables and styles are wired up for you.
22
-
23
- ```ts
24
- // nuxt.config.ts
25
- export default defineNuxtConfig({
26
- modules: ['@juxtui/ui/nuxt'],
27
- })
28
- ```
29
-
30
- **Vue**: import the stylesheet once, then import what you use.
31
-
32
- ```ts
33
- import '@juxtui/ui/style.css'
34
- import { JButton, toast } from '@juxtui/ui'
35
- ```
36
-
37
- Using Tailwind CSS v4? `@import '@juxtui/ui/tailwind.css'` maps the tokens onto utilities (`bg-surface`, `text-fg-muted`, `rounded-md`, `duration-fast`, …). Juxt's styles live in a `juxt` cascade layer, so your own classes always win.
38
-
39
- ## Components
40
-
41
- | | |
42
- | --- | --- |
43
- | **Actions** | Button |
44
- | **Forms** | Input · Textarea · Checkbox · Switch · Select |
45
- | **Overlays** | Dialog · Dropdown Menu · Tooltip · Command Palette |
46
- | **Feedback** | Toast · Badge |
47
- | **Display** | Avatar · Card |
48
- | **Navigation** | Tabs |
49
-
50
- Each one supports light and dark themes, full keyboard use, reduced motion and SSR. Its docs page lists the keys it handles and the ARIA it exposes.
51
-
52
- ## Design philosophy
53
-
54
- - **Quiet until it matters.** The interface is grayscale, and green marks state: focus, checked, success, where you are. Primary buttons use ink, not green.
55
- - **Motion is feedback.** There are four durations (80–240ms) and three curves, and only `transform` and `opacity` animate. Entrances are slower than exits. With reduced motion, things still fade but don't move.
56
- - **Accessible by construction.** Overlays trap focus, restore it when they close, and stack correctly: Escape and outside clicks go to the top-most layer only.
57
- - **Small APIs.** Props are named after what they do, and anything that holds state has a `v-model`. If you know one component, you know the rest.
58
- - **Tokens all the way down.** Colour, type, space, radius, elevation, motion and layering are CSS custom properties. Change `--juxt-accent` and the whole system follows.
59
-
60
- ## License
61
-
62
- MIT
1
+ # juxt.ui
2
+
3
+ **Thoughtfully crafted components for Nuxt & Vue.**
4
+
5
+ juxt.ui is a small component library that cares about the details you feel but rarely notice: how far a menu travels when it opens, where focus goes when a dialog closes, whether a loading button changes width, what happens when you press Escape with a select open inside a dialog.
6
+
7
+ It is Multiple components, one token system and one motion language. It's mostly grayscale, and the green `#079D6B` is kept for state.
8
+
9
+ ```vue
10
+ <JButton>
11
+ Save changes
12
+ </JButton>
13
+ ```
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pnpm add @juxtui/ui
19
+ ```
20
+
21
+ **Nuxt**: add the module. Components, composables and styles are wired up for you.
22
+
23
+ ```ts
24
+ // nuxt.config.ts
25
+ export default defineNuxtConfig({
26
+ modules: ['@juxtui/ui/nuxt'],
27
+ })
28
+ ```
29
+
30
+ **Vue**: import the stylesheet once, then import what you use.
31
+
32
+ ```ts
33
+ import '@juxtui/ui/style.css'
34
+ import { JButton, toast } from '@juxtui/ui'
35
+ ```
36
+
37
+ Using Tailwind CSS v4? `@import '@juxtui/ui/tailwind.css'` maps the tokens onto utilities (`bg-surface`, `text-fg-muted`, `rounded-md`, `duration-fast`, …). juxt.ui's styles live in a `juxt` cascade layer, so your own classes always win.
38
+
39
+ ## Components
40
+
41
+ | | |
42
+ | --- | --- |
43
+ | **Actions** | Button |
44
+ | **Forms** | Input · Textarea · Checkbox · Switch · Select |
45
+ | **Overlays** | Dialog · Dropdown Menu · Tooltip · Command Palette |
46
+ | **Feedback** | Toast · Badge |
47
+ | **Display** | Avatar · Card |
48
+ | **Navigation** | Tabs |
49
+ | **Layout** | Navbar · Burger · Footer · Sidebar |
50
+
51
+ Each one supports light and dark themes, full keyboard use, reduced motion and SSR. Its docs page lists the keys it handles and the ARIA it exposes.
52
+
53
+ ## Design philosophy
54
+
55
+ - **Quiet until it matters.** The interface is grayscale, and green marks state: focus, checked, success, where you are. Primary buttons use ink, not green.
56
+ - **Motion is feedback.** There are four durations (80–240ms) and three curves, and only `transform` and `opacity` animate. Entrances are slower than exits. With reduced motion, things still fade but don't move.
57
+ - **Accessible by construction.** Overlays trap focus, restore it when they close, and stack correctly: Escape and outside clicks go to the top-most layer only.
58
+ - **Small APIs.** Props are named after what they do, and anything that holds state has a `v-model`. If you know one component, you know the rest.
59
+ - **Tokens all the way down.** Colour, type, space, radius, elevation, motion and layering are CSS custom properties. Change `--juxt-accent` and the whole system follows.
60
+
61
+ ## License
62
+
63
+ MIT
@@ -0,0 +1,25 @@
1
+ export interface BurgerProps {
2
+ /** Accessible name while closed. */
3
+ openLabel?: string;
4
+ /** Accessible name while open. */
5
+ closeLabel?: string;
6
+ size?: 'sm' | 'md';
7
+ /** Id of the element this button shows and hides. */
8
+ controls?: string;
9
+ }
10
+ type __VLS_Props = BurgerProps;
11
+ type __VLS_ModelProps = {
12
+ modelValue?: boolean;
13
+ };
14
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
15
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
16
+ "update:modelValue": (value: boolean) => any;
17
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
18
+ "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
19
+ }>, {
20
+ size: "sm" | "md";
21
+ openLabel: string;
22
+ closeLabel: string;
23
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
24
+ declare const _default: typeof __VLS_export;
25
+ export default _default;
@@ -0,0 +1,26 @@
1
+ export interface FooterProps {
2
+ /** Maximum content width; any CSS length. `none` spans the full width. */
3
+ maxWidth?: string;
4
+ /** Hairline above the footer. */
5
+ bordered?: boolean;
6
+ }
7
+ type __VLS_Slots = {
8
+ /** Logo and a short line about the product. */
9
+ brand?: () => unknown;
10
+ /** Link columns, typically `JFooterColumn`s. */
11
+ default?: () => unknown;
12
+ /** The last line: copyright, legal links, social icons. */
13
+ bottom?: () => unknown;
14
+ };
15
+ declare const __VLS_base: import("vue").DefineComponent<FooterProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FooterProps> & Readonly<{}>, {
16
+ maxWidth: string;
17
+ bordered: boolean;
18
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
20
+ declare const _default: typeof __VLS_export;
21
+ export default _default;
22
+ type __VLS_WithSlots<T, S> = T & {
23
+ new (): {
24
+ $slots: S;
25
+ };
26
+ };
@@ -0,0 +1,15 @@
1
+ export interface FooterColumnProps {
2
+ title: string;
3
+ }
4
+ type __VLS_Slots = {
5
+ default?: () => unknown;
6
+ };
7
+ declare const __VLS_base: import("vue").DefineComponent<FooterColumnProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FooterColumnProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
11
+ type __VLS_WithSlots<T, S> = T & {
12
+ new (): {
13
+ $slots: S;
14
+ };
15
+ };
@@ -0,0 +1,47 @@
1
+ export interface NavbarProps {
2
+ /** Stick to the top of the viewport while scrolling. */
3
+ sticky?: boolean;
4
+ /** Hairline under the bar. */
5
+ bordered?: boolean;
6
+ /** Accessible name of the navigation landmark. */
7
+ label?: string;
8
+ /** Width below which links collapse behind the burger. `never` keeps them inline. */
9
+ collapse?: 'sm' | 'md' | 'lg' | 'never';
10
+ /** Maximum content width; any CSS length. `none` spans the full width. */
11
+ maxWidth?: string;
12
+ }
13
+ type __VLS_Props = NavbarProps;
14
+ type __VLS_Slots = {
15
+ /** Logo or product name, usually a link home. */
16
+ brand?: () => unknown;
17
+ /** Navigation links, typically `JNavbarLink`s. Shown inline, or in the mobile menu. */
18
+ default?: () => unknown;
19
+ /** Always visible on the right: search, theme switch, avatar. */
20
+ actions?: () => unknown;
21
+ /** Extra content at the bottom of the mobile menu, e.g. sign-in buttons. */
22
+ 'menu-footer'?: () => unknown;
23
+ };
24
+ type __VLS_ModelProps = {
25
+ /** Mobile menu state. */
26
+ 'open'?: boolean;
27
+ };
28
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
29
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
30
+ "update:open": (value: boolean) => any;
31
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
32
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
33
+ }>, {
34
+ label: string;
35
+ sticky: boolean;
36
+ maxWidth: string;
37
+ bordered: boolean;
38
+ collapse: "sm" | "md" | "lg" | "never";
39
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
40
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
41
+ declare const _default: typeof __VLS_export;
42
+ export default _default;
43
+ type __VLS_WithSlots<T, S> = T & {
44
+ new (): {
45
+ $slots: S;
46
+ };
47
+ };
@@ -0,0 +1,22 @@
1
+ import type { Component } from 'vue';
2
+ export interface NavbarLinkProps {
3
+ /** Marks the current page: styled and announced with `aria-current="page"`. */
4
+ active?: boolean;
5
+ /** Render as another element or component, e.g. `NuxtLink` or `RouterLink`. */
6
+ as?: string | Component;
7
+ }
8
+ type __VLS_Slots = {
9
+ default?: () => unknown;
10
+ leading?: () => unknown;
11
+ };
12
+ declare const __VLS_base: import("vue").DefineComponent<NavbarLinkProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NavbarLinkProps> & Readonly<{}>, {
13
+ as: string | Component;
14
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
18
+ type __VLS_WithSlots<T, S> = T & {
19
+ new (): {
20
+ $slots: S;
21
+ };
22
+ };
@@ -0,0 +1,23 @@
1
+ export interface SidebarProps {
2
+ /** Accessible name of the navigation landmark. */
3
+ label?: string;
4
+ }
5
+ type __VLS_Slots = {
6
+ /** Top of the sidebar: logo, workspace switcher. */
7
+ header?: () => unknown;
8
+ /** Items and groups. */
9
+ default?: () => unknown;
10
+ /** Pinned to the bottom: account, settings, help. */
11
+ footer?: () => unknown;
12
+ };
13
+ declare const __VLS_base: import("vue").DefineComponent<SidebarProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<SidebarProps> & Readonly<{}>, {
14
+ label: string;
15
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
17
+ declare const _default: typeof __VLS_export;
18
+ export default _default;
19
+ type __VLS_WithSlots<T, S> = T & {
20
+ new (): {
21
+ $slots: S;
22
+ };
23
+ };
@@ -0,0 +1,29 @@
1
+ export interface SidebarGroupProps {
2
+ title?: string;
3
+ /** Let the title fold the group away. */
4
+ collapsible?: boolean;
5
+ }
6
+ type __VLS_Props = SidebarGroupProps;
7
+ type __VLS_Slots = {
8
+ default?: () => unknown;
9
+ };
10
+ type __VLS_ModelProps = {
11
+ /** Whether the group is expanded. Open by default. */
12
+ 'open'?: boolean;
13
+ };
14
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
15
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
16
+ "update:open": (value: boolean) => any;
17
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
18
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
19
+ }>, {
20
+ collapsible: boolean;
21
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
23
+ declare const _default: typeof __VLS_export;
24
+ export default _default;
25
+ type __VLS_WithSlots<T, S> = T & {
26
+ new (): {
27
+ $slots: S;
28
+ };
29
+ };
@@ -0,0 +1,26 @@
1
+ import type { Component } from 'vue';
2
+ export interface SidebarItemProps {
3
+ /** Marks the current page: styled and announced with `aria-current="page"`. */
4
+ active?: boolean;
5
+ disabled?: boolean;
6
+ /** Render as another element or component, e.g. `NuxtLink`. */
7
+ as?: string | Component;
8
+ }
9
+ type __VLS_Slots = {
10
+ default?: () => unknown;
11
+ /** Icon before the label. */
12
+ leading?: () => unknown;
13
+ /** After the label: a count, badge or shortcut. */
14
+ trailing?: () => unknown;
15
+ };
16
+ declare const __VLS_base: import("vue").DefineComponent<SidebarItemProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<SidebarItemProps> & Readonly<{}>, {
17
+ as: string | Component;
18
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
20
+ declare const _default: typeof __VLS_export;
21
+ export default _default;
22
+ type __VLS_WithSlots<T, S> = T & {
23
+ new (): {
24
+ $slots: S;
25
+ };
26
+ };
@@ -7,6 +7,7 @@ export { default as JAvatarGroup } from './components/avatar-group/JAvatarGroup.
7
7
  export { default as JBadge } from './components/badge/JBadge.vue';
8
8
  export { default as JBanner } from './components/banner/JBanner.vue';
9
9
  export { default as JBreadcrumb } from './components/breadcrumb/JBreadcrumb.vue';
10
+ export { default as JBurger } from './components/burger/JBurger.vue';
10
11
  export { default as JButton } from './components/button/JButton.vue';
11
12
  export { default as JCard } from './components/card/JCard.vue';
12
13
  export { default as JCheckbox } from './components/checkbox/JCheckbox.vue';
@@ -19,8 +20,12 @@ export { default as JDropdownMenuLabel } from './components/dropdown-menu/JDropd
19
20
  export { default as JDropdownMenuSeparator } from './components/dropdown-menu/JDropdownMenuSeparator.vue';
20
21
  export { default as JDropdownMenuSub } from './components/dropdown-menu/JDropdownMenuSub.vue';
21
22
  export { default as JEmptyState } from './components/empty-state/JEmptyState.vue';
23
+ export { default as JFooter } from './components/footer/JFooter.vue';
24
+ export { default as JFooterColumn } from './components/footer/JFooterColumn.vue';
22
25
  export { default as JInput } from './components/input/JInput.vue';
23
26
  export { default as JKbd } from './components/kbd/JKbd.vue';
27
+ export { default as JNavbar } from './components/navbar/JNavbar.vue';
28
+ export { default as JNavbarLink } from './components/navbar/JNavbarLink.vue';
24
29
  export { default as JPagination } from './components/pagination/JPagination.vue';
25
30
  export { default as JPopover } from './components/popover/JPopover.vue';
26
31
  export { default as JProgress } from './components/progress/JProgress.vue';
@@ -30,6 +35,9 @@ export { default as JRating } from './components/rating/JRating.vue';
30
35
  export { default as JSelect } from './components/select/JSelect.vue';
31
36
  export { default as JSeparator } from './components/separator/JSeparator.vue';
32
37
  export { default as JSheet } from './components/sheet/JSheet.vue';
38
+ export { default as JSidebar } from './components/sidebar/JSidebar.vue';
39
+ export { default as JSidebarGroup } from './components/sidebar/JSidebarGroup.vue';
40
+ export { default as JSidebarItem } from './components/sidebar/JSidebarItem.vue';
33
41
  export { default as JSkeleton } from './components/skeleton/JSkeleton.vue';
34
42
  export { default as JSlider } from './components/slider/JSlider.vue';
35
43
  export { default as JSpinner } from './components/spinner/JSpinner.vue';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  import './styles/index.css';
2
2
  export * from './components';
3
+ export type { BurgerProps } from './components/burger/JBurger.vue';
4
+ export type { FooterProps } from './components/footer/JFooter.vue';
5
+ export type { FooterColumnProps } from './components/footer/JFooterColumn.vue';
6
+ export type { NavbarProps } from './components/navbar/JNavbar.vue';
7
+ export type { NavbarLinkProps } from './components/navbar/JNavbarLink.vue';
8
+ export type { SidebarProps } from './components/sidebar/JSidebar.vue';
9
+ export type { SidebarGroupProps } from './components/sidebar/JSidebarGroup.vue';
10
+ export type { SidebarItemProps } from './components/sidebar/JSidebarItem.vue';
3
11
  export type { AccordionProps } from './components/accordion/JAccordion.vue';
4
12
  export type { AccordionItemProps } from './components/accordion/JAccordionItem.vue';
5
13
  export type { AlertProps, AlertVariant } from './components/alert/JAlert.vue';