Builds React applications with Mantine's 100+ components, hooks, and form library. Use when creating feature-rich UIs with built-in dark mode, accessibility, and TypeScript support.
Builds React UIs with Mantine's 100+ components and 50+ hooks. Use when creating feature-rich interfaces with built-in dark mode, accessibility, and TypeScript support.
/plugin marketplace add mgd34msu/goodvibes-plugin/plugin install goodvibes@goodvibes-marketThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/components.mdreferences/hooks.mdFull-featured React component library with 100+ customizable components and 50+ hooks.
npm install @mantine/core @mantine/hooks
npm install -D postcss postcss-preset-mantine postcss-simple-vars
// postcss.config.cjs
module.exports = {
plugins: {
'postcss-preset-mantine': {},
'postcss-simple-vars': {
variables: {
'mantine-breakpoint-xs': '36em',
'mantine-breakpoint-sm': '48em',
'mantine-breakpoint-md': '62em',
'mantine-breakpoint-lg': '75em',
'mantine-breakpoint-xl': '88em',
},
},
},
};
// App.tsx
import '@mantine/core/styles.css';
import { MantineProvider, createTheme } from '@mantine/core';
const theme = createTheme({
primaryColor: 'blue',
fontFamily: 'Inter, sans-serif',
});
function App() {
return (
<MantineProvider theme={theme}>
<YourApp />
</MantineProvider>
);
}
// Usage
import { Button, TextInput, Group, Stack } from '@mantine/core';
function Demo() {
return (
<Stack>
<TextInput label="Email" placeholder="you@example.com" />
<Group>
<Button>Submit</Button>
<Button variant="outline">Cancel</Button>
</Group>
</Stack>
);
}
import { Button, ActionIcon } from '@mantine/core';
import { IconSettings } from '@tabler/icons-react';
// Variants
<Button variant="filled">Filled</Button>
<Button variant="light">Light</Button>
<Button variant="outline">Outline</Button>
<Button variant="subtle">Subtle</Button>
<Button variant="transparent">Transparent</Button>
<Button variant="white">White</Button>
// Sizes
<Button size="xs">Extra Small</Button>
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
<Button size="xl">Extra Large</Button>
// Colors
<Button color="blue">Blue</Button>
<Button color="red">Red</Button>
<Button color="green">Green</Button>
// With icons
<Button leftSection={<IconSettings size={16} />}>Settings</Button>
<Button rightSection={<IconArrow size={16} />}>Next</Button>
// Loading
<Button loading>Processing...</Button>
// Icon button
<ActionIcon variant="filled" size="lg">
<IconSettings size={18} />
</ActionIcon>
import { TextInput, PasswordInput, Textarea, NumberInput } from '@mantine/core';
<TextInput
label="Email"
description="We'll never share your email"
placeholder="you@example.com"
required
error="Invalid email"
/>
<PasswordInput
label="Password"
placeholder="Enter password"
visible={visible}
onVisibilityChange={toggle}
/>
<Textarea
label="Message"
placeholder="Your message"
autosize
minRows={3}
maxRows={6}
/>
<NumberInput
label="Amount"
placeholder="Enter amount"
min={0}
max={100}
step={5}
prefix="$"
thousandSeparator=","
/>
import { Select, MultiSelect, Combobox } from '@mantine/core';
<Select
label="Framework"
placeholder="Pick one"
data={['React', 'Vue', 'Angular', 'Svelte']}
searchable
clearable
/>
<Select
label="User"
data={[
{ value: '1', label: 'John Doe' },
{ value: '2', label: 'Jane Smith' },
]}
/>
<MultiSelect
label="Technologies"
placeholder="Select technologies"
data={['TypeScript', 'JavaScript', 'Python', 'Rust']}
searchable
maxSelectedValues={3}
/>
import { Modal, Button } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
function ModalDemo() {
const [opened, { open, close }] = useDisclosure(false);
return (
<>
<Modal opened={opened} onClose={close} title="Authentication">
Modal content here...
</Modal>
<Button onClick={open}>Open modal</Button>
</>
);
}
// Drawer
import { Drawer } from '@mantine/core';
<Drawer
opened={opened}
onClose={close}
title="Navigation"
position="left" // left, right, top, bottom
size="md"
>
Drawer content...
</Drawer>
npm install @mantine/notifications
import '@mantine/notifications/styles.css';
import { Notifications, notifications } from '@mantine/notifications';
// Add to app root
<MantineProvider>
<Notifications position="top-right" />
<App />
</MantineProvider>
// Show notification
notifications.show({
title: 'Success',
message: 'Your changes have been saved',
color: 'green',
autoClose: 5000,
});
// Update existing notification
const id = notifications.show({
loading: true,
title: 'Loading...',
message: 'Please wait',
autoClose: false,
});
notifications.update({
id,
loading: false,
title: 'Complete',
message: 'Data loaded successfully',
color: 'green',
autoClose: 3000,
});
import { Tabs } from '@mantine/core';
<Tabs defaultValue="gallery">
<Tabs.List>
<Tabs.Tab value="gallery">Gallery</Tabs.Tab>
<Tabs.Tab value="messages">Messages</Tabs.Tab>
<Tabs.Tab value="settings">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="gallery" pt="md">
Gallery content
</Tabs.Panel>
<Tabs.Panel value="messages" pt="md">
Messages content
</Tabs.Panel>
<Tabs.Panel value="settings" pt="md">
Settings content
</Tabs.Panel>
</Tabs>
import { Card, Image, Text, Badge, Button, Group } from '@mantine/core';
<Card shadow="sm" padding="lg" radius="md" withBorder>
<Card.Section>
<Image src="/product.jpg" height={160} alt="Product" />
</Card.Section>
<Group justify="space-between" mt="md" mb="xs">
<Text fw={500}>Product Name</Text>
<Badge color="pink">On Sale</Badge>
</Group>
<Text size="sm" c="dimmed">
Product description goes here.
</Text>
<Button fullWidth mt="md" radius="md">
Add to Cart
</Button>
</Card>
npm install @mantine/form
import { useForm } from '@mantine/form';
import { TextInput, Button, Stack } from '@mantine/core';
function ContactForm() {
const form = useForm({
mode: 'uncontrolled',
initialValues: {
name: '',
email: '',
message: '',
},
validate: {
name: (value) => value.length < 2 ? 'Name is too short' : null,
email: (value) => /^\S+@\S+$/.test(value) ? null : 'Invalid email',
message: (value) => value.length < 10 ? 'Message too short' : null,
},
});
return (
<form onSubmit={form.onSubmit((values) => console.log(values))}>
<Stack>
<TextInput
label="Name"
placeholder="Your name"
key={form.key('name')}
{...form.getInputProps('name')}
/>
<TextInput
label="Email"
placeholder="your@email.com"
key={form.key('email')}
{...form.getInputProps('email')}
/>
<Textarea
label="Message"
placeholder="Your message"
key={form.key('message')}
{...form.getInputProps('message')}
/>
<Button type="submit">Submit</Button>
</Stack>
</form>
);
}
import { useForm, zodResolver } from '@mantine/form';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(2, 'Name must be at least 2 characters'),
email: z.string().email('Invalid email'),
age: z.number().min(18, 'Must be 18 or older'),
});
const form = useForm({
mode: 'uncontrolled',
initialValues: { name: '', email: '', age: 18 },
validate: zodResolver(schema),
});
import {
useDisclosure,
useToggle,
useDebouncedValue,
useClipboard,
useLocalStorage,
useMediaQuery,
useClickOutside,
useHotkeys,
useForm,
} from '@mantine/hooks';
// Disclosure (modals, menus)
const [opened, { open, close, toggle }] = useDisclosure(false);
// Toggle between values
const [value, toggle] = useToggle(['light', 'dark']);
// Debounced value
const [search, setSearch] = useState('');
const [debounced] = useDebouncedValue(search, 300);
// Clipboard
const clipboard = useClipboard({ timeout: 2000 });
clipboard.copy('Text to copy');
clipboard.copied // true after copy
// Local storage
const [value, setValue] = useLocalStorage({
key: 'user-preference',
defaultValue: 'dark',
});
// Media query
const isMobile = useMediaQuery('(max-width: 768px)');
// Click outside
const ref = useClickOutside(() => setOpened(false));
// Keyboard shortcuts
useHotkeys([
['mod+K', () => openSearch()],
['mod+S', () => saveDocument()],
['Escape', () => closeModal()],
]);
import { createTheme, MantineProvider } from '@mantine/core';
const theme = createTheme({
// Primary color
primaryColor: 'brand',
primaryShade: { light: 6, dark: 8 },
// Custom colors (10 shades each)
colors: {
brand: [
'#e6f2ff', '#b3d9ff', '#80bfff', '#4da6ff', '#1a8cff',
'#0073e6', '#005cb3', '#004480', '#002d4d', '#00161a',
],
},
// Typography
fontFamily: 'Inter, sans-serif',
fontFamilyMonospace: 'Fira Code, monospace',
headings: {
fontFamily: 'Inter, sans-serif',
fontWeight: '600',
sizes: {
h1: { fontSize: '2.5rem', lineHeight: '1.2' },
h2: { fontSize: '2rem', lineHeight: '1.3' },
},
},
// Spacing scale
spacing: {
xs: '0.5rem',
sm: '0.75rem',
md: '1rem',
lg: '1.5rem',
xl: '2rem',
},
// Border radius
radius: {
xs: '2px',
sm: '4px',
md: '8px',
lg: '16px',
xl: '32px',
},
defaultRadius: 'md',
// Shadows
shadows: {
xs: '0 1px 2px rgba(0, 0, 0, 0.05)',
sm: '0 1px 3px rgba(0, 0, 0, 0.1)',
md: '0 4px 6px rgba(0, 0, 0, 0.1)',
lg: '0 10px 15px rgba(0, 0, 0, 0.1)',
xl: '0 20px 25px rgba(0, 0, 0, 0.15)',
},
// Focus styles
focusRing: 'auto', // 'auto' | 'always' | 'never'
// Other
cursorType: 'pointer', // 'default' | 'pointer'
autoContrast: true,
});
import { MantineProvider, useMantineColorScheme, Button } from '@mantine/core';
// Provider setup
<MantineProvider defaultColorScheme="auto">
<App />
</MantineProvider>
// Toggle button
function ColorSchemeToggle() {
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
return (
<Button onClick={() => toggleColorScheme()}>
{colorScheme === 'dark' ? 'Light' : 'Dark'} mode
</Button>
);
}
import { Button, createStyles } from '@mantine/core';
// Using classNames prop
<Button
classNames={{
root: 'my-button-root',
label: 'my-button-label',
}}
>
Styled Button
</Button>
// Using styles prop
<Button
styles={{
root: { backgroundColor: 'red' },
label: { color: 'white' },
}}
>
Inline Styled
</Button>
// CSS Modules (recommended)
// Button.module.css
.root {
background-color: var(--mantine-color-blue-6);
}
.label {
font-weight: 700;
}
// Component
import classes from './Button.module.css';
<Button classNames={classes}>CSS Modules</Button>
// Access theme tokens via CSS variables
.myComponent {
background-color: var(--mantine-color-blue-6);
padding: var(--mantine-spacing-md);
border-radius: var(--mantine-radius-md);
font-size: var(--mantine-font-size-sm);
/* Dark mode specific */
@mixin dark {
background-color: var(--mantine-color-dark-6);
}
/* Responsive */
@media (max-width: $mantine-breakpoint-sm) {
padding: var(--mantine-spacing-xs);
}
}
import {
AppShell,
Container,
Grid,
Group,
Stack,
Flex,
SimpleGrid,
Center,
} from '@mantine/core';
// App shell layout
<AppShell
header={{ height: 60 }}
navbar={{ width: 300, breakpoint: 'sm', collapsed: { mobile: !opened } }}
padding="md"
>
<AppShell.Header>Header</AppShell.Header>
<AppShell.Navbar>Navbar</AppShell.Navbar>
<AppShell.Main>Content</AppShell.Main>
</AppShell>
// Container with max width
<Container size="md">Content</Container>
// Grid
<Grid>
<Grid.Col span={6}>Half</Grid.Col>
<Grid.Col span={6}>Half</Grid.Col>
<Grid.Col span={{ base: 12, md: 6, lg: 4 }}>Responsive</Grid.Col>
</Grid>
// Simple grid (equal columns)
<SimpleGrid cols={3}>
<div>1</div>
<div>2</div>
<div>3</div>
</SimpleGrid>
// Flex layouts
<Group justify="space-between" align="center">
<Text>Left</Text>
<Text>Right</Text>
</Group>
<Stack gap="md">
<Text>Item 1</Text>
<Text>Item 2</Text>
</Stack>
<Flex gap="md" wrap="wrap" justify="center">
<Box>Item</Box>
<Box>Item</Box>
</Flex>
useDisclosure, useDebouncedValue, useForm for common patternsclassNames prop@mantine/form with Zod for type-safe formsThis skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.