A React + Base UI micrographic generator for building dense, print-inspired layouts with rules, glyphs, labels, modules, and exportable SVG, PNG, and animated GIF output.
Install dependencies:
npm installStart the development server:
npm run devThen open:
http://localhost:3000
If port 3000 is busy, Next.js will prompt you to use another port.
Create a production build:
npm run buildRun the production server after building:
npm run startRun unit tests (Jest + React Testing Library):
npm run testRun end-to-end tests (Playwright):
npm run test:e2eThe templates open with animated groups. A spec about editing plain layers
should start from openStillCreator in e2e/still-template.ts, which opens
the default template's layers with no groups and no entrances, rather than
page.goto('/creator').
Typecheck the project:
npm run typecheckLint the project (ESLint with eslint-config-next and the React Hooks rules):
npm run lintnpm run lint fails on warnings as well as errors, and CI runs it on every pull
request.
Prettier owns formatting; its settings live in .prettierrc.json and match the
style the codebase already uses (two spaces, single quotes, semicolons, a
generous 120-column width). eslint-config-prettier switches off ESLint's own
formatting rules, so the two never disagree about the same line.
Format the files you are working on by passing them to the script:
npm run format -- src/components/App.tsx e2e/navigation.spec.tsThe repository predates Prettier and is not formatted end to end yet, so there is deliberately no repo-wide formatting check in CI: running Prettier over everything is a reformatting change of its own.
- Pick a starting point from the
Templatedropdown in the left panel, orStart from scratchfor an empty canvas. Every template opens with a group or two already set to play a simple entrance β the plate's marks popping in row by row, the level bars running out one band at a time β soPlayworks straight away. Change or take off a group's entrance on its row inLayers, or ungroup it to work with its layers one by one. - Add symbols from the right panel. They are grouped into
Dailymarks andTechlogos. Add text from the same panel. - Drag items on the canvas to move them; select one to rotate or resize it with its handles. Resizing keeps the opposite corner of the selection pinned, so the item grows along with the pointer. Select several to align or distribute them; aligning centres works from what each item draws and meets in the middle of the selection, so a label lines up with a mark by its glyphs rather than by its click target. If aligning would push the selection past an edge of the artboard, the whole selection shifts back together, so the items stay aligned with each other. A group aligns and distributes as one thing, by the middle of everything it draws, and keeps its own layout; one group on its own has nothing to line up against, so it gets no align toolbar.
- Select several items and right-click them β on a layer in the
Layerslist, or on the canvas β and chooseGroupto lock them together. The menu offers only what applies:Groupfor loose items,Ungroupfor a group, both only when a group is selected together with other items. Right-clicking a layer or item that is not selected selects it first, so the menu is always about what is under the pointer.Cmd/Ctrl+GandShift+Cmd/Ctrl+Gdo the same from the keyboard. - A group is one row in
Layers. Its>shows the layers inside it; clicking any of them, or any member on the canvas, selects the whole group, and moving, resizing, rotating, copying or deleting the group treats it as one thing. Copying a group gives you a second, independent group rather than enlarging the first. - Groups nest: group a group with other layers, or two groups together, and
they sit inside a new group, each still whole. An open group lists the
groups inside it as rows of their own, each with its own
>. A click anywhere inside still selects the outermost group, andUngrouptakes off one level at a time. - Select items through the
Layerslist in the left panel, and drag a layer up or down the list to change what sits in front of what: the top of the list is the front of the canvas. A line shows where the layer will land, and a group is dragged as one. Inside an open group, drag its layers and the groups inside it among themselves to change which is in front; they stay in the group. The numbers beside the rows count places in the stack, so they stay put while the layers move past them. Undo puts a move back. - Give a layer an entrance with the sparkle button on its row in
Layers: dissolve, slide in from any side, or pop. Set how long it takes and how long it waits first β the wait is what puts a sequence in order, and two layers can share a moment by sharing a delay.Play, above the canvas, previews the whole sequence; it only appears once something has an entrance. - Every group, at any depth, has an entrance of its own on its row, plus
Each next layer: a stagger, so each thing listed in the open group starts that long after the one above it. At 0 they all arrive together. A group inside another takes one turn in it, then staggers its own layers from there, and can play a different movement from the group around it; with none of its own, it plays the outer group's, and its sparkle shows that. With no entrance here, around it or in groups inside it, the stagger is off. Drag the layers inside a group to change who starts first; the timing follows the new order. Ungrouping keeps what everything plays. - Choose a palette from the swatches above the canvas, and zoom with the controls next to them.
- Toggle
GridandInclude backgroundin the right panel, or upload your own background image. - Use the toolbar to randomize, restart the current template, undo/redo, and export. Exports hold the artwork only: selection outlines, the rotate and resize handles, and the alignment toolbar stay in the editor.
Exportopens one dialog with every format in it: SVG, PNG at three sizes, and GIF. When anything on the canvas has an entrance, the dialog says so β only SVG and GIF carry the animation, because a PNG is a single frame.- The PNG sizes are
1x,2xor4xof the 1200 x 800 artboard, so 1200 x 800, 2400 x 1600 or 4800 x 3200 pixels. Every row in the dialog is a button that exports on the spot β there is nothing to select and nothing to confirm, and no size is marked as the current one. The SVG always writes the artboard size, and that file scales to any size wherever it is placed. - Entrances travel with the SVG: the file animates when it is opened in a browser or embedded in a page. They are written as CSS that moves each item away from its own position and back, so a viewer that does not run CSS β and every PNG β shows the finished artwork rather than the first frame.
- The GIF plays the same entrances for anywhere that will not run an SVG. It is always 1200 x 800, since a GIF stores every frame as its own picture, and it holds on the finished artwork for a moment before looping. The option is only offered once at least one layer has an entrance.
- A GIF is always written on the background colour, even with
Include backgroundoff. GIF transparency is one bit β a pixel is either fully clear or fully opaque β and an entrance is made of the in-between, so on a transparent canvas every fade would become a hard cut. The SVG and the PNG both carry real transparency and honour the toggle as usual. - Each export reports back in the corner of the window, naming the file it wrote, or saying what went wrong if the browser could not produce it.
- Your work is saved automatically in the browser, so closing the tab or
reloading brings the canvas, palette, and zoom back as you left them. Use
Restart templateorStart from scratchin the toolbar to discard it. The save is per browser and is skipped silently if the browser blocks storage. - On a laptop-sized screen,
Feedback?under the canvas opens a short form (bug, idea or other, a title and optional details).Continue on GitHubopens a new issue on this repository with it filled in, to check and submit there with a GitHub account. The homepage footer asks for feedback too, with the GitHub mark linking to the repository and a link to open an issue.
- React
- Next.js
- Base UI
- Lucide React icons
- ESLint + Prettier
app/ Next.js routes: the landing page and /creator
src/
components/ React components, App included
templates/ one component per template, plus their layout helpers
hooks/ React hooks: canvas items, history, export, keyboard
lib/ logic with no React state: animations, geometry,
groups, export markup, GIF encoding, persistence,
feedback links
data/ palettes, templates, symbol marks and links
types/ shared types, and declarations for untyped packages
styles.css global styles
e2e/ Playwright tests
Unit tests sit next to the file they cover (groups.ts and groups.test.ts).
