React Markdown Kit ships no styling dependency. Not ZUI, not Tailwind, not a CSS-in-JS runtime, not a design system. A consumer styles the output with whatever they already use.
This is a hard constraint, enforced by the packaging tests in tests/packaging.
- No styling runtime dependency.
dependenciesandpeerDependenciesof no package may contain@zuilib/*,tailwindcss,styled-components,emotion, or any theme provider. The only peers arereactandreact-dom(editor and renderer). - Unstyled by default. With no CSS imported, the renderer emits plain semantic HTML and the editor emits semantic HTML plus the minimum inline style required for function, never for appearance. Nothing has a colour, a font, a radius or a shadow unless the consumer opts in.
- Clean DOM by default, opt-in class hooks. With no
classNamesprop the output is plain semantic HTML with no kit-specific classes, matching whatreact-markdownemits. Styling hooks come from the scope class plus the element (.rmk-document h1), so there is no class soup to fight. When a consumer wants a class on a part they ask for it throughclassNames, andrmk-prefixed defaults appear only for parts that have no semantic element of their own (a variable chip, a task-list item). - No global selectors, no reset. Optional CSS only ever matches inside a
scope class (
.rmk-document,.rmk-editor). No bare element selectors, no*, no:rootoutside a theme block, no!important. - Low specificity. Optional CSS uses single-class selectors so a consumer overrides it with one class of their own and no cascade fight.
- Themeable through CSS custom properties. Every colour, space, radius and
font in the optional CSS reads a
--rmk-*custom property with a fallback. Overriding the property on any ancestor retints the whole thing. - Class names are replaceable.
classNameson the renderer and editor maps a part to the consumer's own class string, so Tailwind, CSS Modules and BEM users never have to fightrmk-defaults. - Components are replaceable.
componentsmaps any element to a consumer component. That is the escape hatch of last resort and must always work.
Every one of these must work without patching the library.
<Markdown>{source}</Markdown>.rmk-document h1 { font-size: 2rem; }import '@react-markdown-kit/renderer/styles.css'
<div className="rmk-document">
<Markdown>{source}</Markdown>
</div>.rmk-document {
--rmk-text: #111;
--rmk-link: rebeccapurple;
--rmk-font-body: 'Inter', system-ui, sans-serif;
}Pass your own classes per part. The rmk- defaults are replaced, not merged,
so there is nothing to !important away.
<Markdown
classNames={{
root: 'prose prose-slate max-w-none',
code: 'rounded bg-slate-100 px-1 py-0.5 font-mono text-sm',
}}
>
{source}
</Markdown><MarkdownEditor
classNames={{
root: 'rounded-lg border border-slate-200',
toolbar: 'flex gap-1 border-b p-2',
content: 'min-h-64 p-4 focus:outline-none',
}}
value={value}
onChange={setValue}
/>Including ZUI, shadcn/ui, MUI, or anything else. This is how a design system is adopted — by choice, from the application, never by the library.
<Markdown components={{ a: AppLink, img: AppImage, code: AppCode }}>
{source}
</Markdown>const editor = useMarkdownEditor({ value, onChange })
<MarkdownEditorProvider editor={editor}>
<MyOwnToolbar /> {/* any component library */}
<MarkdownEditorContent />
</MarkdownEditorProvider>The default editor ships a toolbar so <MarkdownEditor value onChange /> is
useful on its own. That toolbar:
- uses semantic
<button type="button">with accessible names; - carries
rmk-classes and acceptsclassNamesoverrides; - renders no icon font and no icon package — icons are inline SVG in the
package, replaced through the
toolbarrender prop or the headless API (componentsmaps Markdown elements for rendering, not editor chrome); - can be removed entirely with
toolbar={false}or replaced with a render prop.
Nothing about the editor's behaviour depends on its own CSS being loaded. With
@react-markdown-kit/editor/styles.css absent the editor is unstyled but fully
functional, and the packaging test asserts exactly that.
renderer/styles.css is opt-in typography only: readable measure, heading
scale, list indentation, code and table defaults, all inside .rmk-document.
editor/styles.css is opt-in chrome only: the content box, toolbar layout,
focus ring, placeholder, and the editing affordances that have no semantic HTML
equivalent (for example the selected-node outline). All inside .rmk-editor.
Both files must pass scripts/check-css-scope.mjs, which fails the build on a
global selector, an !important, or a hard-coded colour that is not behind a
--rmk-* custom property.