Principles & Guidelines

How to work with Rahhal's Marsam

Operating rules for humans contributing code and AI agents generating it. Following these keeps the platform coherent — and turns "I need a component" from a fork in the road into a clear, documented path.

Hard rules

Non-negotiable. These are the same rules baked into CLAUDE.md at the repo root.

Compose first

Reach for @rahhal/ui before writing your own button. If a wrapper recurs across two or more apps, promote it back into the package.

Tokens come from @rahhal/themes

Never redefine brand colors in an app's Tailwind config. Edit packages/themes/src/themes/{light,dark}.ts and rebuild — never edit the generated CSS / JSON by hand.

Bilingual + RTL by default

EN and AR ship together. Use Tailwind logical utilities (ms-*, me-*, ps-*, pe-*, start-*, end-*) so RTL flips automatically. Locale resolution lives in @rahhal/i18n.

Auth via @rahhal/auth only

Phone OTP is the only sign-in method (standalone OR iOS → web handover). Never import @supabase/supabase-js directly. Never parse the URL fragment manually.

Document deliberately

Shared packages get TSDoc on every export, file headers, and a thorough README. Inline comments only when the why is non-obvious.

No AI attribution in git

Don't add Co-Authored-By Claude (or any AI mention) to commits or PRs. Keep authorship clean.

Need a component that isn't here?

The default answer is not "build it locally and forget" — it's "plan it for the design system from the start." Follow this flow whether you're a human or an agent.

  1. 1

    Search. Confirm it's really missing — filter the catalog at / and grep packages/ui/src. A new variant of an existing component usually belongs as a variant, not a new component.

  2. 2

    Scope. Will more than one mini-app reasonably use this? If yes, plan it for @rahhal/ui from the start. If it's truly one-app-only, keep it app-local — but add a comment with the intended promotion trigger.

  3. 3

    Design the public API. Sketch the props in TypeScript first. Mirror existing conventions: forwardRef, asChild where slot composition makes sense, logical utilities for spacing, design-token classes for color.

  4. 4

    Implement under packages/ui/src/<component>.tsx. Add file-level TSDoc. Export from index.ts. Add a subpath in package.json. The package ships source TSX, not bundled output.

  5. 5

    Add a catalog entry AND a meta row. apps/design-system/src/catalog/entries/<slug>.tsx for the React preview and apps/design-system/src/spec/components-meta.ts for the .md endpoint. Both are required.

  6. 6

    Open one PR. Component + catalog entry + meta + i18n strings should land together. Reviewers can verify the docs match the code in a single pass.

The catalog is the public contract. If a component isn't in the catalog, it doesn't exist for the rest of the platform — and that's how features fragment. Build it here, once, well-documented.

For AI agents

Use this when generating UI for any Rahhal mini-app.

  • Fetch /skill.md first — it lists every catalog page and the cross-platform conventions.
  • Match by token name, not hex. bg-primary is portable across light/dark/RTL; #C2562B is not.
  • Read the relevant /components/<slug>.md before generating. It documents props, variants, and gotchas.
  • Honor RTL. Use logical utilities (ms-*, me-*, etc.) and rtl:rotate-180 on directional icons.
  • Propose new components fully. When something is missing, deliver source + catalog entry + meta row + i18n strings in one change — don't leave the catalog out of sync.

For humans

Code-review checklist when shipping platform changes.

  • Catalog updated? New / renamed components have a catalog/entries/<slug>.tsx AND a row in spec/components-meta.ts.
  • Themes built? Token edits are followed by pnpm --filter @rahhal/themes build so generated CSS / JSON match.
  • RTL tested? Flip the locale switcher to AR; logical utilities should mirror the layout without manual flipping.
  • i18n strings in both locales? New copy lands in both en.json and ar.json.
  • TSDoc on shared exports? Every public symbol in packages/* gets a one-line summary, params, and an example for components / hooks.