Skip to content

Commit 9305dbd

Browse files
committed
feat: Add new Material 3 components including app bar, badge, bottom sheet, chip, list, menu, navigation drawer, progress, tabs, and tooltip.
1 parent e4b7d65 commit 9305dbd

23 files changed

Lines changed: 719 additions & 2 deletions

File tree

src/components/app-bar/index.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* Material Design 3 App Bar Component - Tailwind Plugin
3+
*/
4+
5+
import plugin from 'tailwindcss/plugin';
6+
7+
export const appBarPlugin: ReturnType<typeof plugin> = plugin(function ({ addComponents }) {
8+
const appBars = {
9+
// Top App Bar Container
10+
'.md-top-app-bar': {
11+
'@apply sticky top-0 left-0 right-0 z-40 flex items-center gap-4 px-4 h-16 bg-md-surface': {},
12+
'@apply transition-all duration-200': {},
13+
14+
// Scrolled state (optional, adds elevation/color change)
15+
'&.scrolled': {
16+
'@apply bg-md-surface-container shadow-md-2': {},
17+
},
18+
19+
// Small App Bar
20+
'&.md-top-app-bar-small': {
21+
'@apply h-16': {},
22+
},
23+
// Medium App Bar (expands)
24+
'&.md-top-app-bar-medium': {
25+
'@apply h-[112px] flex-col items-start justify-end pb-6': {},
26+
'.md-top-app-bar-title': {
27+
'@apply text-md-headline-small': {},
28+
}
29+
},
30+
// Large App Bar
31+
'&.md-top-app-bar-large': {
32+
'@apply h-[152px] flex-col items-start justify-end pb-7': {},
33+
'.md-top-app-bar-title': {
34+
'@apply text-md-headline-medium': {},
35+
}
36+
},
37+
38+
// Leading Icon (Menu/Back)
39+
'.md-top-app-bar-leading': {
40+
'@apply p-3 -ml-3 rounded-md-full text-md-on-surface hover:bg-md-on-surface/10 cursor-pointer': {},
41+
},
42+
43+
// Title
44+
'.md-top-app-bar-title': {
45+
'@apply flex-1 text-md-title-large text-md-on-surface truncate': {},
46+
},
47+
48+
// Trailing Actions Container
49+
'.md-top-app-bar-actions': {
50+
'@apply flex items-center gap-2 -mr-2': {},
51+
},
52+
53+
// Action Icon
54+
'.md-top-app-bar-action': {
55+
'@apply p-3 rounded-md-full text-md-on-surface-variant hover:bg-md-on-surface/10 cursor-pointer': {},
56+
}
57+
},
58+
59+
// Bottom App Bar
60+
'.md-bottom-app-bar': {
61+
'@apply fixed bottom-0 left-0 right-0 z-40 flex items-center justify-between gap-4 px-4 h-20 bg-md-surface-container': {},
62+
// FAB usually lives here or floats above
63+
'.md-fab': {
64+
'@apply absolute right-4 bottom-4 shadow-md-3': {}, // Or inline if wanted
65+
}
66+
}
67+
};
68+
69+
addComponents(appBars);
70+
});
71+
72+
export default appBarPlugin;

src/components/app-bar/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export interface TopAppBarOptions {
2+
title: string;
3+
variant?: 'small' | 'medium' | 'large' | 'center-aligned';
4+
scrollBehavior?: 'pinned' | 'hide' | 'shrink';
5+
}
6+
7+
export interface BottomAppBarOptions {
8+
fab?: boolean;
9+
}

src/components/badge/index.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Material Design 3 Badge Component - Tailwind Plugin
3+
*/
4+
5+
import plugin from 'tailwindcss/plugin';
6+
7+
export const badgePlugin: ReturnType<typeof plugin> = plugin(function ({ addComponents }) {
8+
const badges = {
9+
'.md-badge': {
10+
'@apply inline-flex items-center justify-center min-w-[16px] h-4 px-1 rounded-md-full bg-md-error text-md-on-error text-[11px] font-medium leading-none': {},
11+
12+
// Small dot variant
13+
'&.md-badge-dot': {
14+
'@apply min-w-[6px] w-[6px] h-[6px] p-0': {},
15+
},
16+
17+
// Large variant (for more than 3 digits, though max 999+ usually)
18+
'&.md-badge-large': {
19+
'@apply h-6 min-w-[24px] px-2 text-md-label-small': {},
20+
},
21+
22+
// Positioned contextual usage (e.g. on icon)
23+
// Parent needs relative
24+
'.relative > &': {
25+
'@apply absolute top-0 right-0 -mr-1 -mt-1': {}, // Simple corner positioning
26+
}
27+
}
28+
};
29+
30+
addComponents(badges);
31+
});
32+
33+
export default badgePlugin;

src/components/badge/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface BadgeOptions {
2+
label?: string | number;
3+
variant?: 'small' | 'large' | 'dot';
4+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Material Design 3 Bottom Sheet Component - Tailwind Plugin
3+
*/
4+
5+
import plugin from 'tailwindcss/plugin';
6+
7+
export const bottomSheetPlugin: ReturnType<typeof plugin> = plugin(function ({ addComponents }) {
8+
const sheets = {
9+
// Bottom Sheet Container/Modal
10+
'.md-bottom-sheet': {
11+
'@apply fixed bottom-0 left-0 right-0 z-50 bg-md-surface-container-low text-md-on-surface rounded-t-md-xl shadow-md-4 transition-transform duration-300 translate-y-full max-h-[90vh] flex flex-col': {},
12+
13+
// Standard/Persistent variant (not fixed, usually inline or absolute in container)
14+
'&.md-bottom-sheet-standard': {
15+
'@apply relative z-0 translate-y-0 shadow-none border-t border-md-outline-variant w-full': {},
16+
},
17+
18+
// Open state
19+
'&.open': {
20+
'@apply translate-y-0': {},
21+
},
22+
23+
// Drag Handle
24+
'.md-bottom-sheet-handle': {
25+
'@apply w-8 h-1 bg-md-on-surface-variant/40 rounded-full mx-auto my-4 shrink-0': {},
26+
},
27+
28+
// Content area
29+
'.md-bottom-sheet-content': {
30+
'@apply px-4 pb-4 overflow-y-auto': {},
31+
},
32+
33+
// Modal Scrim
34+
'+ .md-bottom-sheet-scrim': {
35+
'@apply fixed inset-0 z-40 bg-black/32 transition-opacity duration-300 opacity-0 pointer-events-none': {},
36+
'&.open': {
37+
'@apply opacity-100 pointer-events-auto': {},
38+
}
39+
}
40+
}
41+
};
42+
43+
addComponents(sheets);
44+
});
45+
46+
export default bottomSheetPlugin;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface BottomSheetOptions {
2+
open?: boolean;
3+
variant?: 'standard' | 'modal';
4+
}

src/components/chip/index.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* Material Design 3 Chip Component - Tailwind Plugin
3+
*/
4+
5+
import plugin from 'tailwindcss/plugin';
6+
7+
export const chipPlugin: ReturnType<typeof plugin> = plugin(function ({ addComponents }) {
8+
const chips = {
9+
// Base Chip
10+
'.md-chip': {
11+
'@apply inline-flex items-center gap-2 px-3 h-8 border border-md-outline rounded-md-lg cursor-pointer transition-all duration-200': {},
12+
'@apply text-md-label-large text-md-on-surface bg-transparent': {}, // Assist/Suggestion default
13+
14+
// Hover state
15+
'@apply hover:bg-md-on-surface/10 hover:shadow-md-1': {},
16+
17+
// Focus state
18+
'&:focus-visible': {
19+
'@apply outline-none ring-2 ring-md-primary ring-offset-2': {},
20+
},
21+
22+
// Disabled
23+
'&:disabled, &.disabled': {
24+
'@apply opacity-38 cursor-not-allowed shadow-none border-md-on-surface/12 pointer-events-none': {},
25+
},
26+
27+
// Icons
28+
'> svg': {
29+
'@apply w-4.5 h-4.5': {}, // 18px
30+
'&.md-chip-icon-leading': {
31+
'@apply text-md-primary': {},
32+
},
33+
'&.md-chip-icon-trailing': {
34+
'@apply text-md-on-surface-variant hover:text-md-on-surface': {},
35+
}
36+
},
37+
38+
// Avatar/Image
39+
'.md-avatar': {
40+
'@apply w-6 h-6 rounded-md-full -ml-1': {},
41+
},
42+
43+
// Variants
44+
45+
// Elevated Chip
46+
'&.md-chip-elevated': {
47+
'@apply bg-md-surface-container-low border-none shadow-md-1': {},
48+
'@apply hover:bg-md-surface-container hover:shadow-md-2': {},
49+
},
50+
51+
// Filter Chip (Selected)
52+
'&.active': {
53+
'@apply bg-md-secondary-container border-transparent text-md-on-secondary-container': {},
54+
'@apply hover:bg-md-secondary-container hover:shadow-md-1 hover:brightness-95': {},
55+
56+
// Using pseudo for checkmark in pure CSS is tricky without structure,
57+
// expecting SVG icon for checked state usually.
58+
'.md-chip-icon-leading': {
59+
'@apply text-md-on-secondary-container': {},
60+
}
61+
},
62+
63+
// Input Chip (has close button often)
64+
'&.md-chip-input': {
65+
'@apply pr-2': {}, // Less padding on right for remove icon
66+
}
67+
}
68+
};
69+
70+
addComponents(chips);
71+
});
72+
73+
export default chipPlugin;

src/components/chip/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface ChipOptions {
2+
label: string;
3+
variant?: 'assist' | 'filter' | 'input' | 'suggestion';
4+
elevated?: boolean;
5+
active?: boolean; // For filter chips
6+
icon?: string;
7+
}

src/components/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,13 @@ export { snackbarPlugin } from './snackbar';
1010
export { checkboxPlugin } from './checkbox';
1111
export { radioPlugin } from './radio';
1212
export { switchPlugin } from './switch';
13+
export { tabsPlugin } from './tabs';
14+
export { navigationDrawerPlugin } from './navigation-drawer';
15+
export { appBarPlugin } from './app-bar';
16+
export { listPlugin } from './list';
17+
export { chipPlugin } from './chip';
18+
export { badgePlugin } from './badge';
19+
export { progressPlugin } from './progress';
20+
export { tooltipPlugin } from './tooltip';
21+
export { bottomSheetPlugin } from './bottom-sheet';
22+
export { menuPlugin } from './menu';

src/components/list/index.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* Material Design 3 List Component - Tailwind Plugin
3+
*/
4+
5+
import plugin from 'tailwindcss/plugin';
6+
7+
export const listPlugin: ReturnType<typeof plugin> = plugin(function ({ addComponents }) {
8+
const lists = {
9+
// List Container
10+
'.md-list': {
11+
'@apply py-2 bg-md-surface text-md-on-surface rounded-md-xs overflow-hidden': {},
12+
},
13+
14+
// List Item
15+
'.md-list-item': {
16+
'@apply relative flex items-center gap-4 px-4 min-h-[56px] cursor-pointer transition-colors': {},
17+
'@apply text-md-body-large text-md-on-surface': {},
18+
'@apply hover:bg-md-on-surface/10': {}, // State layer
19+
20+
// Active state
21+
'&.active': {
22+
'@apply bg-md-secondary-container text-md-on-secondary-container': {},
23+
},
24+
25+
// Disabled state
26+
'&:disabled, &.disabled': {
27+
'@apply opacity-38 cursor-not-allowed pointer-events-none': {},
28+
},
29+
30+
// Leading content (Icon/Avatar)
31+
'.md-list-item-leading': {
32+
'@apply flex items-center justify-center text-md-on-surface-variant': {},
33+
'> svg': {
34+
'@apply w-6 h-6': {},
35+
},
36+
// Avatar support
37+
'.md-avatar': {
38+
'@apply w-10 h-10 rounded-md-full bg-md-primary-container text-md-on-primary-container flex items-center justify-center text-md-title-medium font-medium': {},
39+
'> img': {
40+
'@apply w-full h-full object-cover rounded-md-full': {},
41+
}
42+
}
43+
},
44+
45+
// Headline/Scanning text content
46+
'.md-list-item-content': {
47+
'@apply flex-1 flex flex-col justify-center': {},
48+
},
49+
'.md-list-item-headline': {
50+
'@apply text-md-body-large text-md-on-surface': {},
51+
},
52+
'.md-list-item-supporting-text': {
53+
'@apply text-md-body-medium text-md-on-surface-variant': {},
54+
},
55+
'.md-list-item-trailing-supporting-text': {
56+
'@apply text-md-label-small text-md-on-surface-variant': {},
57+
},
58+
59+
// Trailing content (Icon/Checkbox/Switch)
60+
'.md-list-item-trailing': {
61+
'@apply flex items-center justify-center text-md-on-surface-variant': {},
62+
'> svg': {
63+
'@apply w-6 h-6': {},
64+
}
65+
}
66+
}
67+
};
68+
69+
addComponents(lists);
70+
});
71+
72+
export default listPlugin;

0 commit comments

Comments
 (0)