本文へ移動
cccskills
無料GitHub で公開

web-ui-mantine

Mantine v7 component library — theming, styling, hooks, forms, notifications

インストール方法を見る

含まれるファイル(7)

  • SKILL.md14.1 KB
  • examples/components.md5.5 KB
  • examples/core.md8.1 KB
  • examples/forms.md8.4 KB
  • examples/theming.md6.1 KB
  • metadata.yaml483 B
  • reference.md8.7 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Mantine v7 Component Patterns

Quick Guide: Mantine is a batteries-included React component library whose v7 rewrite replaced Emotion with CSS Modules, so there is no CSS-in-JS runtime. Wrap the app in MantineProvider with a createTheme object, install postcss-preset-mantine for the mixins and light-dark(), and style through one of four routes: style props for one-offs, the Styles API (classNames / styles) for inner elements, CSS Modules for custom components, and theme.components for app-wide defaults. @mantine/form, @mantine/hooks, @mantine/notifications and @mantine/dates are separate packages, each with its own stylesheet.

Detailed Resources:

  • examples/core.md — provider and PostCSS setup, SSR, CSS Modules, Styles API, style props, AppShell, loading states
  • examples/components.md — Modal, Drawer, Menu, Tabs, Accordion, notifications, dates
  • examples/theming.md — custom colours, virtualColor, component defaults, auto-contrast, theme merging
  • examples/forms.md — useForm validation, cross-field rules, nested fields, dynamic lists, schema resolvers
  • reference.md — component selection tree, style-prop and mixin tables, Styles API selectors, package list, anti-patterns

<critical_requirements>

Before writing Mantine code

Wrap the app in MantineProvider and import @mantine/core/styles.css at the root. Every component reads its CSS variables from the provider, and without the stylesheet they render as unstyled markup rather than failing loudly.

Install and configure postcss-preset-mantine. It compiles @mixin smaller-than, @mixin hover, light-dark() and rem(). Without it those lines pass through to the browser as invalid CSS and are dropped.

Write custom component styles as CSS Modules. createStyles was removed in v7, and the theme's values are available to plain CSS as --mantine-* custom properties.

Give a custom colour all ten shades. The palette is indexed 0 (lightest) to 9 (darkest) and the type is a 10-tuple, so a short array is a compile error rather than a runtime surprise.

</critical_requirements>


Auto-detection: Mantine, @mantine/core, @mantine/hooks, @mantine/form, @mantine/notifications, @mantine/dates, MantineProvider, createTheme, virtualColor, useMantineColorScheme, useComputedColorScheme, ColorSchemeScript, mantineHtmlProps, useDisclosure, useDebouncedValue, postcss-preset-mantine, light-dark(), getInputProps, form.key(), classNames prop, styles prop, Component.extend()

Applies to:

  • Building on Mantine's 100+ components, and customising them through the theme rather than by forking them
  • Choosing between style props, the Styles API, CSS Modules and theme defaults for a given override
  • Form state with @mantine/form — validation, nested paths, dynamic lists
  • Colour scheme handling, including the SSR flash and the auto resolution trap
  • The utility hooks in @mantine/hooks

Handled elsewhere:

  • CSS methodology and design-token architecture beyond Mantine's own theme — the theme publishes --mantine-* custom properties, and how the rest of your stylesheet is organised is settled elsewhere
  • Server state — components take data as props, and where it was fetched is not their concern
  • Global client state beyond a component's own — useDisclosure is a local boolean, not a store
  • Schema definition — useForm accepts a resolver, and the schema library that produces it is settled by whatever owns validation

<philosophy>

Mantine is pre-built rather than copy-and-own: you configure components through a theme, not by holding their source. Two consequences shape everything below.

The theme is the customisation surface. Colours, radii, fonts, breakpoints and per-component default props all live in one createTheme object, and every component reads them as CSS variables. An override written at the call site is a decision that will need repeating.

Every component exposes its internals by name. The Styles API gives each inner element a selector — input, label, error, dropdown — reachable through classNames and styles without wrapping or forking the component. That is why v7 could drop CSS-in-JS: nothing needs to run at render time to target an inner node.

</philosophy>

<decision_framework>

Which styling route

One-off spacing or colour            → style props: p="md" bg="blue.1"
Every instance of a component        → theme.components with Component.extend()
An inner element, with pseudo-classes → classNames + CSS Modules
An inner element, quick and inline   → styles prop (no pseudo-classes)
A component of your own              → CSS Modules using var(--mantine-*)
Responsive, in JSX                   → style props with object syntax: w={{ base: "100%", sm: 400 }}
Responsive, in CSS                   → @mixin smaller-than / larger-than
Light/dark conditional               → light-dark() or @mixin light / @mixin dark

Full component-selection tree and the Styles API selector table: reference.md.

</decision_framework>


<patterns>

Core patterns

Pattern 1: Provider and theme

import "@mantine/core/styles.css";
import { createTheme, MantineProvider } from "@mantine/core";

const theme = createTheme({ primaryColor: "blue", defaultRadius: "md" });

export function App() {
  return <MantineProvider theme={theme}>{/* app */}</MantineProvider>;
}

Create the theme at module scope — inline in the JSX it is a new object every render, and every consumer re-renders with it. Server rendering additionally needs ColorSchemeScript in <head> and mantineHtmlProps on <html>, or the first paint flashes the wrong scheme.

Full code, including postcss.config.cjs: examples/core.md


Pattern 2: Components share one prop vocabulary

<Button variant="light" color="red" size="md" radius="sm" loading={isSaving} leftSection={<Icon />}>
  Delete
</Button>
<TextInput label="Email" placeholder="you@example.com" withAsterisk error={errors.email} />
<Select label="Role" data={["Admin", "Editor", "Viewer"]} searchable clearable />
<Group gap="md">{/* row */}</Group>
<Stack gap="sm">{/* column */}</Stack>

variant, size, color and radius mean the same thing on every component, and Group / Stack replace hand-written flexbox. loading, withAsterisk and error are states the component already renders — reimplementing them costs the built-in accessibility wiring.


Pattern 3: Overlays and compound components

const [opened, { open, close }] = useDisclosure(false);

<Modal opened={opened} onClose={close} title="Confirm action" centered>
  <Button onClick={close}>Cancel</Button>
</Modal>;

Overlays are controlled by opened / onClose, and useDisclosure is the handler set they expect. Compound components — Menu.Target / Menu.Dropdown, Tabs.Tab / Tabs.Panel, Accordion.Item — pair their parts by value, so reordering the list cannot desync them.

Full code: examples/components.md


Pattern 4: Styling — the four routes

// Style props: theme values, not CSS values
<Box p="md" bg="blue.1" c="blue.9" fz="sm" w={{ base: "100%", sm: 400 }} />

// Styles API: named inner elements
<TextInput classNames={{ input: classes.input, label: classes.label }} />
<TextInput styles={{ input: { backgroundColor: "var(--mantine-color-blue-0)" } }} />
/* your-component.module.css */
.card {
  padding: var(--mantine-spacing-md);
  background: light-dark(
    var(--mantine-color-white),
    var(--mantine-color-dark-6)
  );

  @mixin hover {
    box-shadow: var(--mantine-shadow-md);
  }
  @mixin smaller-than $mantine-breakpoint-sm {
    padding: var(--mantine-spacing-sm);
  }
}

classNames takes classes and so supports pseudo-classes; styles takes inline objects and does not. Both target the same named selectors, listed per component in reference.md.

Full code: examples/core.md


Pattern 5: Theming

const theme = createTheme({
  colors: {
    brand: [...BRAND_COLORS], // exactly 10, index 0 lightest
    surface: virtualColor({ name: "surface", dark: "dark", light: "gray" }),
  },
  primaryColor: "brand",
  primaryShade: { light: 6, dark: 8 },
  components: {
    Button: Button.extend({ defaultProps: { variant: "filled", size: "sm" } }),
  },
});

primaryShade picks a different index per scheme, so one colour name reads correctly in both. virtualColor goes further and swaps the whole palette. Component.extend() sets defaults for every instance and is type-checked against that component's props.

Full code: examples/theming.md


Pattern 6: Colour scheme

const { setColorScheme } = useMantineColorScheme();
const computed = useComputedColorScheme("light");

<ActionIcon
  onClick={() => setColorScheme(computed === "dark" ? "light" : "dark")}
>
  {computed === "dark" ? <SunIcon /> : <MoonIcon />}
</ActionIcon>;

Read the scheme through useComputedColorScheme, not through useMantineColorScheme().colorScheme. The second returns the literal setting, which is "auto" for the default — so a toggle branching on it treats a dark system as light. lightHidden and darkHidden cover the render-one-or-the-other case without a hook at all.

Full code: examples/theming.md


Pattern 7: Forms

const form = useForm({
  mode: "uncontrolled",
  initialValues: { email: "", termsAccepted: false },
  validate: { email: (v) => (/^\S+@\S+$/.test(v) ? null : "Invalid email") },
});

<TextInput label="Email" withAsterisk key={form.key("email")} {...form.getInputProps("email")} />
<Checkbox label="I accept" key={form.key("termsAccepted")}
  {...form.getInputProps("termsAccepted", { type: "checkbox" })} />

getInputProps binds value, change handler and error in one spread; { type: "checkbox" } switches it to checked. mode: "uncontrolled" is the default and keeps keystrokes out of React's render path — which is why form.key() is required alongside it, to give React a key that survives the value changing outside its knowledge. Nested paths use dot notation (address.city, members.0.name), and list items move through insertListItem / removeListItem.

Full code, including schema resolvers: examples/forms.md


Pattern 8: Hooks

const [opened, { open, close, toggle }] = useDisclosure(false);
const [debounced] = useDebouncedValue(search, 300);
const isMobile = useMediaQuery("(max-width: 48em)");
const ref = useClickOutside(() => close());
const clipboard = useClipboard({ timeout: 2000 }); // clipboard.copied stays true for the timeout

useDebouncedValue and useClickOutside own their own cleanup. useMediaQuery returns undefined during server rendering rather than guessing, so branch on that third state instead of treating it as false.


Pattern 9: Notifications

<Notifications position="top-right" />; // once, inside MantineProvider

const id = notifications.show({
  loading: true,
  message: "Uploading…",
  autoClose: false,
});
notifications.update({
  id,
  loading: false,
  message: "Uploaded",
  color: "green",
  autoClose: 3000,
});

The renderer mounts once; the API is imperative and callable from anywhere, including outside React. Update by the returned id rather than showing a second notification.

Full code: examples/components.md


Pattern 10: Dates

import "@mantine/dates/styles.css";

<DatesProvider settings={{ locale: "en", firstDayOfWeek: 0 }}>
  <DatePickerInput
    type="range"
    label="Date range"
    value={range}
    onChange={setRange}
  />
</DatesProvider>;

Separate package, separate stylesheet, dayjs as a peer. type (default / range / multiple) changes the value's shape rather than the component.

Full code: examples/components.md

</patterns>

<red_flags>

Red flags

Breaks at runtime:

  • No @mantine/core/styles.css import — components render as unstyled markup with no error
  • No postcss-preset-mantine — @mixin, light-dark() and rem() reach the browser as invalid CSS and are dropped, so responsive and dark-mode rules silently do nothing
  • A package's own stylesheet left out — @mantine/dates/styles.css, @mantine/notifications/styles.css and the rest are separate imports
  • createStyles — removed in v7
  • A custom colour with fewer than ten shades — a TypeScript error, since the palette type is a 10-tuple
  • createTheme(...) inline in the JSX — a new theme object every render, re-rendering every consumer

Surprising behaviour:

  • useMantineColorScheme().colorScheme returns "auto" when that is the setting, so a toggle branching on it misreads a dark system as light — useComputedColorScheme resolves it
  • form.key() is not optional in uncontrolled mode; without it inputs keep stale values after a reset
  • Style props take theme values, not CSS values: p="md" is theme.spacing.md, and color="blue.6" is shade 6 of the palette rather than a CSS colour
  • The PostCSS breakpoint variables are a second copy of theme.breakpoints — change one and the CSS and the JS disagree with nothing to catch it
  • useMediaQuery returns undefined during server rendering, which is a third state rather than false
  • light-dark() here is compiled by the PostCSS preset rather than the native CSS function
  • The style prop reaches only the outermost element and takes raw CSS values, so a colour written there is frozen against the colour scheme — inner elements need classNames or styles, and a theme-aware colour needs bg / c or a --mantine-color-* variable
  • v8 shipped in May 2025. @mantine/dates moved from Date objects to "YYYY-MM-DD" strings, DatesProvider dropped timezone, and CodeHighlight dropped highlight.js. This skill documents v7; check the migration guide before upgrading.

</red_flags>

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

agents-inc のスキルをすべて見る

このスキルの問題を報告する