@grupo-elo-editorial/shared-ui-react

v1.1.0

Published

Grupo ELO Editorial — React component library. Multibrand-aware primitives (atoms/molecules/organisms/layout) and domain components, built on Radix UI + shadcn/ui, consuming @grupo-elo-editorial/design-tokens@^3.1.0. Tailwind CSS v4 bundled.

Readme

@grupo-elo-editorial/shared-ui-react

React component library for the Grupo ELO Editorial multibrand design system. Brand-aware primitives, composed components, layout patterns, and domain components — all rendering against the same CSS-variable contract as @grupo-elo-editorial/design-tokens v3+.

Stack: React 18+ · TypeScript · Tailwind CSS v4 · Radix UI (via shadcn/ui primitives) · Lucide icons · Sonner toasts · class-variance-authority for variants.

This is the React sibling of @grupo-elo-editorial/shared-ui-vue (Vue 3). Both packages share the same semantic component surface and consume the same design tokens.

Architecture position

shared-ui-react ───────► design-system (tokens, themes)
apps/<consumer> ──► shared-ui-react + design-system

shared-ui-react never imports from app code or from shared-ui-vue.

Install

pnpm add @grupo-elo-editorial/shared-ui-react @grupo-elo-editorial/design-tokens

The package's dependencies field declares every Radix UI primitive and helper (lucide-react, class-variance-authority, clsx, tailwind-merge, sonner, next-themes, cmdk, embla-carousel-react, input-otp, react-day-picker, react-hook-form, react-resizable-panels, recharts, vaul) so a fresh consumer install resolves everything in one step.

Use

// main.tsx — once per app
import '@grupo-elo-editorial/shared-ui-react/style.css';

// The stylesheet ships Lato + Tailwind CSS v4 + multibrand theme tokens.
// Brand + theme switch via attributes on <html> (or a scoped container):
//
//   <html data-brand="elo-editora" data-theme="light"> ... </html>
//   <html data-brand="perabook"    data-theme="dark"> ... </html>
import { Button, ProductCard, Hero, Modal, TopAccessibilityBar } from '@grupo-elo-editorial/shared-ui-react';

export function Example() {
  return (
    <>
      <TopAccessibilityBar />
      <Hero
        title="Histórias que acompanham gerações"
        subtitle="Mais de 30 anos publicando literatura infantojuvenil"
        ctaLabel="Conheça o catálogo"
      />
      <Button variant="primary" size="lg">Comprar agora</Button>
    </>
  );
}

Component inventory (42)

Atoms (7 custom + 6 re-exported)

| Custom | Re-exported (shadcn/ui) | |---|---| | Button (primary/secondary/ghost/danger/link/success × sm/md/lg) | Input | | Textarea | Select, SelectContent, SelectItem, SelectTrigger, SelectValue | | Badge (default/secondary/success/warning/danger/info/outline) | Checkbox | | Avatar (sm/md/lg/xl + status) | RadioGroup, RadioGroupItem | | Spinner (sm/md/lg/xl) | Toggle (= Switch) | | Divider (horizontal/vertical, solid/dashed/dotted) | | | Rating (interactive + readonly + half-stars) | |

Molecules (5 custom + 4 re-exported)

| Custom | Re-exported | |---|---| | ProductCard (default/skeleton/out-of-stock variants) | Breadcrumb* | | SearchBar (icon, clear, loading) | Accordion* | | PriceDisplay (original price + discount + installments) | Tooltip* | | QuantitySelector (min/max constraints) | ProgressBar (@radix-ui/react-progress) | | FormGroup (label/required/error/helper) | toast (sonner) |

Organisms (4 custom + 2 re-exported)

| Custom | Re-exported | |---|---| | Hero (default/centered/split variants) | Modal (= Dialog from shadcn) | | EmptyState (default/search/cart/error) | Drawer (= Sheet from shadcn) | | ErrorState (404/500/network/generic) | | | ProductGrid (responsive 2–5 columns) | |

PRD-specific (2)

  • TopAccessibilityBar — dark-mode toggle, A+/A- font controls, VLibras lazy-load.
  • HeroCarousel — responsive <picture> with srcset, autoplay + pause on hover, respects prefers-reduced-motion.

Utility

  • cn(...) — class-name merger (clsx + tailwind-merge).

Brand + theme contract

The bundled stylesheet defines tokens at four selectors:

| Selector | Effect | |---|---| | :root or [data-brand="elo-editora"] or [data-brand="elo-editora"][data-theme="light"] | Elo Editora — light (default) | | [data-brand="elo-editora"][data-theme="dark"] | Elo Editora — dark | | [data-brand="perabook"] or [data-brand="perabook"][data-theme="light"] | PeraBook — light | | [data-brand="perabook"][data-theme="dark"] | PeraBook — dark |

This matches the data-brand / data-theme contract from @grupo-elo-editorial/design-tokens v3.

// Apply globally
document.documentElement.setAttribute('data-brand', 'elo-editora');
document.documentElement.setAttribute('data-theme', 'dark');

// Or scope to a container
<div data-brand="perabook" data-theme="light">
  <ProductCard ... />
</div>

Source layout

packages/shared-ui-react/
├── src/
│   ├── index.ts                            ← public API barrel (imports CSS)
│   ├── components/
│   │   ├── atoms/{Button,Avatar,Badge,…}.tsx
│   │   ├── molecules/{ProductCard,SearchBar,FormGroup,…}.tsx
│   │   ├── organisms/{Hero,EmptyState,ErrorState,ProductGrid}.tsx
│   │   ├── prd/{HeroCarousel,TopAccessibilityBar}.tsx
│   │   ├── ui/                              ← shadcn/ui primitives (Radix wrappers)
│   │   └── figma/ImageWithFallback.tsx
│   └── styles/{fonts,theme,index}.css       ← bundled into dist/style.css
├── playground/                              ← Vite dev preview (pnpm run play)
├── docs/
│   ├── COMPONENT_LIBRARY.md                 ← original Figma export documentation
│   ├── CORRECTIONS.md                       ← Figma export correction notes
│   └── GUIDELINES.md                        ← original Figma project guidelines
├── vite.config.ts                           ← Tailwind v4 + library build, externals
├── tsconfig.{json,build.json}
├── package.json                             ← @grupo-elo-editorial/[email protected]
└── README.md

Development

# From the monorepo root
pnpm install                                                       # workspace install

# From this package
pnpm --filter @grupo-elo-editorial/shared-ui-react type-check      # ts check, no emit
pnpm --filter @grupo-elo-editorial/shared-ui-react build           # builds dist/
pnpm --filter @grupo-elo-editorial/shared-ui-react play            # Vite playground preview

Conventions

  • React 18+ with function components and hooks. No class components.
  • Naming: component names PascalCase (e.g. Button, not RButton — the scope already namespaces). File names PascalCase.tsx.
  • Variants: defined via class-variance-authority (cva). Re-exported alongside the component (e.g. buttonVariants).
  • Styling: Tailwind v4 utilities consuming var(--brand-*) / shadcn semantic tokens. Never hardcoded hex.
  • Brand awareness: components don't reach for brand state — they consume CSS variables. Brand switching is the consumer's concern (data-brand / data-theme on root or scoped container).
  • Accessibility: WCAG 2.2 AA — focus-visible, keyboard nav, ARIA labels on icon-only buttons, focus traps in modals (provided by Radix), respects prefers-reduced-motion.
  • Strict TypeScript: every component exports its prop interface (e.g. ButtonProps).

Origin

Initial component set seeded from the Figma Make multibrand export. See docs/COMPONENT_LIBRARY.md for the full inventory the Figma project shipped and docs/CORRECTIONS.md for adjustments applied during the export. The historical Figma React preview app is preserved at design-system/_explorations/figma-multibrand/ for visual reference.

Sibling — Vue 3

@grupo-elo-editorial/shared-ui-vue is the Vue 3 sibling. Same component semantics, same tokens, same brand contract — a Button in React and a DSButton in Vue render identically.

Publish

Releases go through the NPM Release GitHub Actions workflow. See design-system/RELEASE.md for the full release procedure.

License

UNLICENSED — proprietary to Grupo ELO Editorial.