@thomagotchi/react-modal-oc
v0.1.0
Published
A lightweight, dependency-free confirmation modal for React, built with createPortal (no third-party modal plugin).
Maintainers
Readme
@thomagotchi/react-modal-oc
A lightweight confirmation modal for React — built from scratch with
createPortal, no third-party
modal library (no react-modal, no jQuery). Zero runtime dependencies besides React.
This component was extracted from the HRnet React
conversion project (OpenClassrooms), where it replaces the legacy jQuery
jQuery.modal.js plugin used to confirm employee creation.
Features
- No dependencies other than
react/react-dom(peer dependencies only) - Renders through a portal into
document.body, so it's never clipped by a parent'soverflow: hiddenorz-indexstacking context - Closes on Escape, on backdrop click, or programmatically
- Locks background scroll while open
- Accessible by default:
role="dialog",aria-modal="true", labelled heading - Fully themeable via CSS custom properties, with sensible defaults out of the box
Installation
npm install @thomagotchi/react-modal-ocreact and react-dom (v18 or later) are peer dependencies and must already be
installed in your project.
Usage
import { useState } from 'react';
import ConfirmModal from '@thomagotchi/react-modal-oc';
import '@thomagotchi/react-modal-oc/style.css';
function App() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button onClick={() => setIsOpen(true)}>Save employee</button>
<ConfirmModal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Success"
>
Employee has been created!
</ConfirmModal>
</>
);
}A named export is also available if you prefer explicit imports:
import { ConfirmModal } from '@thomagotchi/react-modal-oc';Props
| Prop | Type | Default | Required | Description |
| ---------- | ----------------- | ---------------- | -------- | -------------------------------------------------------------------- |
| isOpen | boolean | — | Yes | Controls whether the modal is rendered/visible. |
| onClose | () => void | — | Yes | Called when the user closes the modal (Escape, backdrop click, or the OK/close buttons). |
| title | string | 'Confirmation' | No | Heading text shown in the modal header. |
| children | React.ReactNode | — | No | Content rendered inside the modal body. |
The component is intentionally uncontrolled beyond isOpen/onClose — it does not manage
its own open state, so the parent stays in full control of when it renders.
Theming
Styles are shipped as plain, prefixed (rmo-*) CSS classes with CSS custom properties
that fall back to a default look, so the modal renders correctly with zero
configuration. Override any of the following on :root (or any ancestor of the modal)
to theme it:
| Variable | Default |
| -------------------------------- | --------------------------------- |
| --rmo-color-primary | #1d4e89 |
| --rmo-color-primary-dark | #163a66 |
| --rmo-color-surface | #ffffff |
| --rmo-color-text | #2c3e50 |
| --rmo-color-on-primary | #ffffff |
| --rmo-overlay-bg | rgba(0, 0, 0, 0.45) |
| --rmo-radius-sm / -md / -lg| 4px / 8px / 12px |
| --rmo-spacing-sm … -2xl | 0.5rem … 3rem |
| --rmo-font-size-base / -lg | 1rem / 1.125rem |
| --rmo-max-width | 420px |
| --rmo-z-index | 999 |
Example — theming to a dark surface:
:root {
--rmo-color-surface: #1e1e1e;
--rmo-color-text: #f0f0f0;
--rmo-color-primary: #7c3aed;
--rmo-color-primary-dark: #6025c2;
}Why a custom component instead of a plugin?
This package intentionally avoids wrapping an existing modal library. It only implements
the actual UI behavior of a modal dialog (open/close, focus trapping via the portal,
Escape/backdrop dismissal) — matching the scope of what it replaces: jQuery.modal.js's
UI plugin, not any application/business logic that used to sit alongside it.
Local development
npm install
npm run dev # serves the example/ app for manual testing
npm run build # builds dist/ (ESM + CJS + style.css)License
MIT © Thomagotchi
