Skip to content

About

Design a business card in the browser and walk away with files a print shop will actually accept.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

Business Card Generator

Design a business card in the browser and walk away with files a print shop will actually accept.

Most free card makers hand you a picture of a card. Enlarge it and it goes soft; send it to a printer and they ask for something else. This one draws the card as a true vector, so the same design exports as a crisp image for your email signature, a print-ready PDF for a professional printer, and a tiled sheet you can run off on an office printer and cut up the same afternoon.

Everything happens inside the browser. There is no account to create, no upload, and no server holding anyone's contact details.


What it does

Two finished designs Studio, a modern asymmetric layout, and Mono, a centred classical one. Each ships a light and a dark colourway.
Your details, your styling Name, role, company, contact, tagline and tags — then control the colour of every element and the typeface, size, weight and slant of every line.
Your brand Upload a logo (SVG, PNG or JPEG). Pick from three bundled typefaces or a curated Google Fonts catalogue.
A working QR code Point it at a website, or generate a contact card that adds you straight to someone's phone.
Four ways out High-resolution PNG, print-ready PDF, editable SVG, and a multi-up sheet with cut guides.
Nothing to lose Your card saves in your browser as you type. You can also save it to a file and reopen it later, or hand it to a colleague.

Who it is for

Anyone who needs a card and does not have a designer: founders, freelancers, consultants, small teams. It is deliberately not a free-form design tool — you pick a finished layout and fill it in, so the result looks considered without requiring taste or time.


Why the vector decision matters

This is the one choice everything else follows from, and it is worth understanding before changing anything.

The card is drawn as SVG — a set of shapes and text, not a grid of pixels. That single drawing feeds the on-screen preview and every export, so what you see cannot drift from what you get.

The commercial consequences:

  • A printer can scale it. Vector artwork stays sharp at any size, so the PDF is genuinely press-ready rather than a screenshot in a PDF wrapper.
  • The text stays text. Open the exported PDF and you can select the words. That is the proof it is vector.
  • Resolution is a choice, not a limit. The PNG can be generated at whatever density is asked for, because it is rendered fresh each time.

It also made the product simpler. Rendering to vector removed the need for a screenshot library and a PDF library — the PNG is a canvas draw and the PDF is the browser's own print engine. Fewer dependencies, less to break.


Running it

Locally

cp .env.example .env.development   # then set the two ports
cd code && npm install && npm run dev

The .env files live at the repository root. Both Docker Compose and Vite read them, so there is one source of truth and nothing to keep in sync.

Open the URL printed in the terminal.

With Docker

Both stacks run from the repository root.

# Development — hot reload
docker compose --env-file .env.development \
  -f docker-compose.dev.yml up

# Production — built and served by nginx
cp .env.example .env        # then set FRONTEND_PORT
docker compose -f docker-compose.prod.yml up -d --build

Production needs no --env-file: Compose auto-loads a file named exactly .env, which is the production environment file.

Development does need it, because Compose only auto-loads that one name. env_file: inside the compose file passes variables into the container, while the ${...} substitutions are resolved by Compose beforehand — two different mechanisms. Forget the flag and the dev stack stops with a clear error rather than quietly starting on the production port.

Do not run the Docker dev stack and a local npm run dev at the same time — the port is claimed strictly, so the second one fails loudly rather than silently picking another.

Setting the domain

VITE_SITE_URL in the .env files is the public origin, with no trailing slash. It drives the canonical link, the Open Graph and Twitter tags, and the generated robots.txt and sitemap.xml — so the domain is written in one place rather than scattered through markup and static files.

.env.development   VITE_SITE_URL=http://localhost:$FRONTEND_PORT
.env               VITE_SITE_URL=https://your-domain.example

Set the production value before the first deploy. Until then the social previews and sitemap will point at a placeholder.

Commands

Command What it does
npm run dev Development server with hot reload
npm run lint ESLint across the project
npx tsc --noEmit Type check
npm test Unit tests (Vitest)
npm run build Type check, then production build

Lint and type check run after every unit of work. The production build runs only when the owner asks for it.


How it is built

A Vite + React + TypeScript single-page application. No backend, no database, no authentication — the product needs none of them, and adding them would mean holding other people's contact details for no benefit.

Area Choice
Framework React 19, TypeScript (strict), Vite 8
Styling Tailwind v4 plus a fluid design-token system
State One useCardData hook; no global store
Validation Zod — the schema is the single source of truth
Storage localStorage, validated on read
QR qrcode.react, rendered as SVG inside the card
Serving nginx in production

Repository layout

.
├── code/                     # the application
│   ├── src/
│   │   ├── components/card/  # the SVG card and its text fitting
│   │   ├── components/editor/# the control panels
│   │   ├── lib/card/         # schema, layouts, vCard
│   │   ├── lib/fonts/        # registry, Google fetch, export embedding
│   │   └── lib/export/       # PNG, print sheet, file save
│   ├── Dockerfile.dev
│   ├── Dockerfile.prod
│   └── nginx.conf
├── docs/
│   ├── ARCHITECTURE.md       # the implementation contract — read first
│   ├── Vite-Build-Template.md
│   └── Frontend-UI-Motion-Tooling-Standard.md
├── docker-compose.dev.yml
└── docker-compose.prod.yml

Two rules that protect the output

Both are easy to break by accident and both cause the export to stop matching the preview:

  1. Nothing on the card reads the app's light/dark setting. Card colours come from the card's own data. If the card followed the interface theme, switching to dark mode would silently change the file you export.
  2. Animation stops at the card's edge. Motion belongs to the editor. The exported file has no animation, so moving artwork would make the preview disagree with the output.

Search and social

The head carries a descriptive title, meta description, canonical link, Open Graph and Twitter Card tags, WebApplication structured data, a web manifest, and a full icon set. robots.txt and sitemap.xml are generated at build time from VITE_SITE_URL.

One honest caveat: this is a client-rendered single-page app, so what a crawler indexes is the static head above — not the editor, which only exists after JavaScript runs. That is the right trade for a tool (there is no content to rank beyond the landing page), but it does mean SEO effort belongs in the head and the social card, not in the app itself.

The one genuine technical risk

When a typeface is used on a card, that font file has to be packed inside the export. A font is not a colour — it is a file, and a PNG or PDF that merely references it will fall back to a different typeface on someone else's machine. Worse, it fails silently.

The app therefore collects every face in use and embeds it before rasterising. The Font embedding proof panel exists to verify this: pick a face, export the proof, and confirm it matches the preview exactly.


Documentation

  • docs/ARCHITECTURE.md — the implementation contract: data model, layout rules, font pipeline, build order and verification.
  • Agenthandoff.md — current state, decisions and watchouts. Read before changing anything.
  • STACK.md — what is actually installed, scanned from the manifests.
  • Changelog.md — release history.

Status and limitations

Version 1 is implemented end to end. Known boundaries, all deliberate:

  • Two layouts. More are additive; the layout contract holds without a template language.
  • No bleed or crop marks. Cards export at trim size. Bleed can be added by composing the trim card into a larger canvas — the path is open.
  • Local system fonts are best-effort. The browser feature that reads fonts off your machine exists only in Chrome and Edge. Where it is missing, the option is hidden rather than offered and broken.
  • Uploaded raster logos stay raster. An SVG logo stays sharp at any print size; a PNG or JPEG is a photograph and will soften when enlarged. The app warns rather than blocks.
  • No accounts, no sharing service. Cards move between people as files.

© 2026 Triffix Solutions. All rights reserved.

About

Design a business card in the browser and walk away with files a print shop will actually accept.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages