@breathe-ds/ui 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +219 -142
- package/components/Accordion.tsx +17 -14
- package/components/AnchorNav.tsx +33 -21
- package/components/Avatar.tsx +37 -0
- package/components/BackgroundVideo.tsx +14 -14
- package/components/BottomNav.tsx +70 -69
- package/components/Breadcrumb.tsx +45 -0
- package/components/BreatheMark.tsx +12 -24
- package/components/Button.tsx +12 -12
- package/components/Byline.tsx +103 -0
- package/components/CaptionBand.tsx +74 -0
- package/components/Card.tsx +5 -5
- package/components/Container.tsx +4 -4
- package/components/CtaCard.tsx +52 -0
- package/components/Curtain.tsx +152 -0
- package/components/FactStrip.tsx +55 -0
- package/components/Footer.tsx +169 -77
- package/components/Header.tsx +32 -17
- package/components/Heading.tsx +86 -0
- package/components/InfoCard.tsx +116 -0
- package/components/LanguageSwitcher.tsx +139 -0
- package/components/List.tsx +81 -0
- package/components/Lockup.tsx +38 -55
- package/components/MapCard.tsx +158 -0
- package/components/MediaCard.tsx +266 -0
- package/components/PageVideoHero.tsx +53 -53
- package/components/PersonChip.tsx +49 -0
- package/components/Pill.tsx +42 -0
- package/components/PillList.tsx +21 -48
- package/components/ProfessionalCard.tsx +341 -0
- package/components/ProfessionalModal.tsx +243 -0
- package/components/Rows.tsx +131 -0
- package/components/SectionKicker.tsx +3 -3
- package/components/TestimonialCard.tsx +138 -0
- package/components/Text.tsx +31 -0
- package/components/Timeline.tsx +69 -0
- package/components/VideoInviteCard.tsx +168 -0
- package/components/VideoStoryCard.tsx +382 -0
- package/components/icons.tsx +6 -6
- package/components/marks/creeta.tsx +22 -22
- package/components/marks/ttcab.tsx +13 -13
- package/package.json +28 -5
- package/theme.css +88 -22
- package/tokens/creeta.css +14 -12
- package/tokens/neutral.css +16 -10
- package/tokens/ttcab.css +9 -8
- package/components/Cortina.tsx +0 -152
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Felipe Genuino
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,197 +1,274 @@
|
|
|
1
1
|
# Breathe
|
|
2
2
|
|
|
3
|
-
**Breathe** —
|
|
4
|
-
creeta, ttcab, metodorta…).
|
|
5
|
-
|
|
3
|
+
**Breathe** — the design system of the **Cuidado Global family** (the RTA / Mariangela
|
|
4
|
+
ecosystem: creeta, ttcab, metodorta…). The sites **inherit** this package and apply
|
|
5
|
+
their own palette on top of the same contract.
|
|
6
6
|
|
|
7
|
-
>
|
|
7
|
+
> npm package: `@breathe-ds/ui` (the technical address). **Breathe** is the system's name.
|
|
8
8
|
|
|
9
|
-
> **v0.1** — tokens +
|
|
10
|
-
>
|
|
9
|
+
> **v0.1** — tokens + components (see `components/`) + the sites' brand marks.
|
|
10
|
+
> See everything rendered in the playground (below).
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## Idea
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
creeta
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
The family's sites **do not share a palette** (ttcab is teal/cream,
|
|
15
|
+
creeta is teal/mist/magenta). What they share are the **token roles** —
|
|
16
|
+
`surface`, `ink`, `action`, `line`… — with the same names and structure.
|
|
17
|
+
Each site defines the values (`--cg-*`) of its own palette.
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
A token is never "color X": it is **role + measured contrast**. (Felipe is color-blind;
|
|
20
|
+
color decisions are always made by role and contrast, never by eye.)
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## Install
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
```bash
|
|
25
|
+
npm i @breathe-ds/ui
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Public package on npm: https://www.npmjs.com/package/@breathe-ds/ui · source at
|
|
29
|
+
https://github.com/felipegenuino/breathe-ds (MIT license). The components ship as TSX source (no
|
|
30
|
+
build step): in Next, add `transpilePackages: ["@breathe-ds/ui"]` to `next.config`.
|
|
31
|
+
|
|
32
|
+
## Usage (Tailwind v4)
|
|
33
|
+
|
|
34
|
+
In the site's `globals.css`:
|
|
25
35
|
|
|
26
36
|
```css
|
|
27
37
|
@import "tailwindcss";
|
|
28
38
|
@import "@breathe-ds/ui/theme.css";
|
|
29
|
-
@import "@breathe-ds/ui/tokens/ttcab.css"; /*
|
|
39
|
+
@import "@breathe-ds/ui/tokens/ttcab.css"; /* or define the --cg-* inline */
|
|
30
40
|
```
|
|
31
41
|
|
|
32
|
-
|
|
42
|
+
Then Tailwind utilities use the roles: `bg-surface`, `text-ink`,
|
|
33
43
|
`border-line`, `bg-action`, `text-action-text`, `rounded-2xl`, etc.
|
|
34
44
|
|
|
35
|
-
|
|
45
|
+
For a site without a ready-made token file, define the roles on `:root` itself:
|
|
36
46
|
|
|
37
47
|
```css
|
|
38
48
|
:root {
|
|
39
49
|
--cg-surface: #fbf5e9;
|
|
40
50
|
--cg-ink: #144753;
|
|
41
|
-
/* …
|
|
51
|
+
/* … see tokens/ttcab.css for the full list */
|
|
42
52
|
}
|
|
43
53
|
```
|
|
44
54
|
|
|
45
|
-
|
|
55
|
+
`theme.css` also sets the family's base behaviour: smooth scrolling for anchors
|
|
56
|
+
(`html { scroll-behavior: smooth }`, switched off under `prefers-reduced-motion`)
|
|
57
|
+
and `scroll-padding` (6rem top, 7rem bottom) so keyboard focus never lands
|
|
58
|
+
hidden behind the sticky Header or the BottomNav (WCAG 2.4.11).
|
|
46
59
|
|
|
47
|
-
|
|
60
|
+
## Token roles
|
|
61
|
+
|
|
62
|
+
| Token (Tailwind) | Role | `--cg-*` |
|
|
48
63
|
|---|---|---|
|
|
49
|
-
| `surface` |
|
|
50
|
-
| `surface-panel` |
|
|
51
|
-
| `
|
|
52
|
-
| `ink
|
|
53
|
-
| `ink-
|
|
54
|
-
| `
|
|
55
|
-
| `action
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
| `surface` | base background | `--cg-surface` |
|
|
65
|
+
| `surface-panel` | dark panel (footer, final CTA) | `--cg-surface-panel` |
|
|
66
|
+
| `card` | raised surface: cards, pills, menus over the page (white on all three palettes) | `--cg-card` |
|
|
67
|
+
| `ink` | primary text / headings | `--cg-ink` |
|
|
68
|
+
| `ink-soft` | secondary text | `--cg-ink-soft` |
|
|
69
|
+
| `ink-body` | reading body text | `--cg-ink-body` |
|
|
70
|
+
| `action` | CTA / highlight — **never plain text** | `--cg-action` |
|
|
71
|
+
| `action-text` | text in the action color (AA) | `--cg-action-text` |
|
|
72
|
+
| `line` | hairlines / borders | `--cg-line` |
|
|
73
|
+
| `font-heading` / `font-body` | typography | `--cg-font-*` |
|
|
74
|
+
| `radius-xl/2xl/pill` · `ease-cg` | radius and motion curve | fixed |
|
|
75
|
+
|
|
76
|
+
## Measured contrast — TTCAB (`tokens/ttcab.css`)
|
|
77
|
+
|
|
78
|
+
Values from the standard WCAG formula (the same ones `preview/` computes live):
|
|
79
|
+
|
|
80
|
+
| Pair | Contrast | Level |
|
|
65
81
|
|---|---|---|
|
|
66
|
-
| surface `#fbf5e9` / ink `#144753` | 9
|
|
67
|
-
| surface `#fbf5e9` / ink-soft `#54707a` | 4
|
|
68
|
-
| surface `#fbf5e9` / ink-body `#1e4f5b` | 8
|
|
69
|
-
| surface `#fbf5e9` / action-text `#9a5d07` | 4
|
|
70
|
-
| surface-panel `#144753` /
|
|
82
|
+
| surface `#fbf5e9` / ink `#144753` | 9.41:1 | AAA |
|
|
83
|
+
| surface `#fbf5e9` / ink-soft `#54707a` | 4.86:1 | AA |
|
|
84
|
+
| surface `#fbf5e9` / ink-body `#1e4f5b` | 8.32:1 | AAA |
|
|
85
|
+
| surface `#fbf5e9` / action-text `#9a5d07` | 4.90:1 | AA |
|
|
86
|
+
| surface-panel `#144753` / cream text | 9.41:1 | AAA |
|
|
71
87
|
|
|
72
|
-
`action` `#f6a22a`
|
|
88
|
+
`action` `#f6a22a` is decorative/CTA only — do not use it as text on cream.
|
|
73
89
|
|
|
74
|
-
##
|
|
90
|
+
## Family typography
|
|
75
91
|
|
|
76
|
-
|
|
92
|
+
Decision (2026-10-28): **open-source fonts only** — no licensed ones (Gotham is out).
|
|
77
93
|
|
|
78
|
-
|
|
|
94
|
+
| Role | Font | Note |
|
|
79
95
|
|---|---|---|
|
|
80
|
-
| `font-heading` | **Quicksand** |
|
|
81
|
-
| `font-body` | **Figtree** |
|
|
82
|
-
|
|
83
|
-
**Hierarquia/escala tipográfica:** usar a do **novo.creeta** como guia (bem trabalhada
|
|
84
|
-
e refinada). _A extrair para tokens de tamanho/peso/entrelinha._
|
|
85
|
-
|
|
86
|
-
## Primeiros consumidores
|
|
96
|
+
| `font-heading` | **Quicksand** | headings, whole family (Google Fonts) |
|
|
97
|
+
| `font-body` | **Figtree** | body, whole family (Google Fonts) |
|
|
87
98
|
|
|
88
|
-
**
|
|
89
|
-
|
|
99
|
+
**Type hierarchy/scale:** use **novo.creeta**'s as the guide (well worked out
|
|
100
|
+
and refined). _To be extracted into size/weight/line-height tokens._
|
|
90
101
|
|
|
91
|
-
##
|
|
102
|
+
## First consumers
|
|
92
103
|
|
|
93
|
-
**
|
|
94
|
-
|
|
104
|
+
**ttcab** and **creeta**. The creeta palette in `tokens/creeta.css` is
|
|
105
|
+
illustrative (marked `[conferir]`) — to be finalized when creeta migrates.
|
|
95
106
|
|
|
96
|
-
|
|
97
|
-
corpo, em qualquer tema — inclusive o Neutro) e os **ícones** (lucide). Botões
|
|
98
|
-
usam a fonte de corpo semibold (o 600 da Quicksand rende leve demais).
|
|
107
|
+
## What changes per theme (and what is fixed)
|
|
99
108
|
|
|
100
|
-
|
|
109
|
+
**Changes per theme:** only **colors** and **graphic devices** (ThreadRail, OrganicFrame,
|
|
110
|
+
watermark…). Each site/brand in the family has its own color and its own graphic devices.
|
|
101
111
|
|
|
102
|
-
|
|
103
|
-
|
|
112
|
+
**Fixed across the whole family:** **typography** (Quicksand for headings + Figtree for
|
|
113
|
+
body, in any theme — including Neutral) and the **icons** (lucide). Buttons
|
|
114
|
+
use the body font at semibold (Quicksand's 600 renders too light).
|
|
104
115
|
|
|
105
|
-
##
|
|
116
|
+
## Decision — card style
|
|
106
117
|
|
|
107
|
-
|
|
118
|
+
The DS card language: **subtle borders + subtle shadow** (reference: "Famílias contam" on novo.creeta). No strong borders or heavy shadows. _To become `--shadow-card` + `--border` tokens._
|
|
108
119
|
|
|
109
|
-
##
|
|
120
|
+
## Decision — hero width
|
|
110
121
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
—
|
|
122
|
+
**Wider / full-bleed** heroes, in **novo.creeta**'s proportion (more immersive, less
|
|
123
|
+
side margin). Applies to `PageVideoHero` and `PageHero`. ttcab's current hero is more contained
|
|
124
|
+
— widen it to the creeta standard when migrating. _Becomes a hero-width token/scale._
|
|
114
125
|
|
|
115
|
-
##
|
|
126
|
+
## Site brand marks (`components/marks/`)
|
|
116
127
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
`
|
|
128
|
+
Each site in the family has its real symbol and wordmark, copied from the brand
|
|
129
|
+
files (`public/brand`) in the repositories, split into two components so the
|
|
130
|
+
`Curtain` and the `Lockup` can compose the arrangements:
|
|
120
131
|
|
|
121
|
-
| Site |
|
|
132
|
+
| Site | Symbol | Wordmark | Import |
|
|
122
133
|
|---|---|---|---|
|
|
123
|
-
| TTCAB | `TtcabSymbol` (
|
|
124
|
-
| Creeta | `CreetaSymbol` (
|
|
134
|
+
| TTCAB | `TtcabSymbol` (mother + baby, orange `#F6A22A`) | `TtcabWordmark` (uppercase, `currentColor`) | `@breathe-ds/ui/components/marks/ttcab` |
|
|
135
|
+
| Creeta | `CreetaSymbol` (three waves + dot, brand colors) | `CreetaWordmark` (lowercase, `currentColor`) | `@breathe-ds/ui/components/marks/creeta` |
|
|
125
136
|
|
|
126
137
|
```tsx
|
|
127
|
-
<
|
|
138
|
+
<Curtain logo={<TtcabSymbol />} name={<TtcabWordmark />} wordmarkRatio={TTCAB_LOCKUP.wordmarkPerSymbol} tagline="…" />
|
|
128
139
|
<Lockup variant="horizontal" size="md" logo={<CreetaSymbol />} wordmark={<CreetaWordmark />} wordmarkRatio={CREETA_LOCKUP.wordmarkPerSymbol} name="creeta" />
|
|
129
140
|
```
|
|
130
141
|
|
|
131
|
-
**
|
|
132
|
-
`*_LOCKUP.wordmarkPerSymbol` (
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
`color`
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
`Lockup`
|
|
142
|
-
novo.creeta:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
##
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
`
|
|
142
|
+
**The proportion comes from the brand manual, not from the eye.** Each brand file exports
|
|
143
|
+
`*_LOCKUP.wordmarkPerSymbol` (wordmark height ÷ symbol height in the
|
|
144
|
+
horizontal logo: TTCAB 0.557 · creeta 0.469) and `symbolSide` (in TTCAB the
|
|
145
|
+
symbol sits to the **right** of the name; in creeta and the others, to the left). The `Lockup` and the `Curtain`
|
|
146
|
+
pass this to the svg through the `--cg-wordmark` variable; the injected symbol takes the
|
|
147
|
+
height of the size (`size`). Measured on the sites (2026-10-29): in both headers the
|
|
148
|
+
symbol is **44px** tall — that is the Lockup's `size="md"`. The symbol accepts
|
|
149
|
+
`color` for a monochrome version (white on a dark panel). The artwork is never
|
|
150
|
+
edited by hand: it is the one from each brand's manual.
|
|
151
|
+
|
|
152
|
+
`Lockup` with `entrance` (and `entranceDelay` in ms) reproduces the header entrance of
|
|
153
|
+
novo.creeta: symbol, then name, then tagline; with reduced motion it becomes
|
|
154
|
+
a short fade.
|
|
155
|
+
|
|
156
|
+
## Typography
|
|
157
|
+
|
|
158
|
+
The family's scale, from novo.creeta, as components: `Heading` (sizes
|
|
159
|
+
`display` 2.5rem→6xl · `hero` · `section` 1.75rem→4xl · `card` · `small`;
|
|
160
|
+
Quicksand medium in `ink`) with an optional `accent` — the second sentence of
|
|
161
|
+
the same heading on its own line, one tone below in `ink-soft` — and
|
|
162
|
+
`SectionHeading` (eyebrow/number line, title + accent, lead). `Text` for
|
|
163
|
+
paragraphs (`lead`, `body`, `small`, `eyebrow`, Figtree) and `Prose` for a run
|
|
164
|
+
of paragraphs at reading width. Two tones, one element: the hierarchy comes
|
|
165
|
+
from color, not from extra markup.
|
|
166
|
+
|
|
167
|
+
## Navigation
|
|
168
|
+
|
|
169
|
+
- **Header**: brand (the app passes the link to the home page) · items · item with dropdown
|
|
170
|
+
(icons on sub-items; final link with a → arrow, or ↗ if `external`) · language · CTA.
|
|
171
|
+
Site convention: "Home" is just the house (`icon` + `iconOnly`), with a label for
|
|
172
|
+
screen readers.
|
|
173
|
+
- **BottomNav**: on phone/tablet, navigation leaves the header and becomes the
|
|
174
|
+
floating bottom bar. It is the code of novo.creeta's BarraNavegacao (= ttcab's
|
|
175
|
+
BottomNav), made neutral through tokens: it receives ALL pages in order of importance
|
|
176
|
+
(`pages`, with the lucide `Icon`) and shows as many as fit (64px per tab,
|
|
177
|
+
ResizeObserver); the rest and the external exits (`extras`, with the domain spelled out
|
|
178
|
+
and a ↗ arrow) go to the "More" panel, a native popover (Esc, tap outside, no
|
|
179
|
+
JS). Gradient veil behind the bar. `hideFrom` = `md` (creeta) or `lg`
|
|
180
|
+
(ttcab). `labels` for another language. `embedded` for the demo frame.
|
|
181
|
+
- **AnchorNav**: floating capsule of page anchors (scroll-spy + sliding
|
|
182
|
+
indicator), extracted from ttcab's TreatmentAnchorNav. Target sections need
|
|
183
|
+
a `scroll-mt-*` compatible with the sticky header.
|
|
184
|
+
- **LanguageSwitcher**: the SeletorDeIdioma of novo.creeta — `menu` (button with
|
|
185
|
+
the code, opens the three languages; header) or `list` (plain links; the Footer
|
|
186
|
+
uses it). Same page in the other language, each language written in itself,
|
|
187
|
+
current marked by ✓ and `aria-current`.
|
|
188
|
+
- **List**: the family's list with a marker in front of each item (novo.creeta):
|
|
189
|
+
`marker="check"` (24px bubble, "what we assess / what we treat") or
|
|
190
|
+
`marker="arrow"` (corner arrow, one sentence per item, as in the Footer
|
|
191
|
+
notice). An item with `href` is a link: semibold, underline, outbound arrow.
|
|
192
|
+
- **Timeline**: the "Como cuidamos" of novo.creeta — ordered steps, each with a
|
|
193
|
+
44px icon bubble, a title and a paragraph; a thin line links the drawings. A
|
|
194
|
+
step may end with a link (strong link + outbound arrow).
|
|
195
|
+
- **RowList** / **CollapseRow** / **LinkRow**: the list of equal rows at the foot
|
|
196
|
+
of a card (the Trajectory / Guidance / Instagram block of the ProfessionalCard),
|
|
197
|
+
generic. 48px rows with hairlines; a CollapseRow is a native `<details>` with a
|
|
198
|
+
text or a list of links (`links`, count beside the label); a LinkRow goes
|
|
199
|
+
somewhere (↗ another page, ↓ anchor, `external` for a new tab, `icon` to
|
|
200
|
+
replace the arrow). The ProfessionalCard is built on them.
|
|
201
|
+
- **ProfessionalCard** / **ProfessionalModal** / **CaptionBand**: the team card of
|
|
202
|
+
novo.creeta — portrait (plain, living clip on hover, or with the "Presentation"
|
|
203
|
+
chip), name, role, registration (**always**: COFFITO 414/2012), trajectory /
|
|
204
|
+
guidance / Instagram rows; the modal plays the presentation with sound, the
|
|
205
|
+
live caption band (active WebVTT cue) above the name, and the full bio.
|
|
206
|
+
- **Byline** / **PersonChip** / **Avatar**: who signs or explains content
|
|
207
|
+
(novo.creeta) — stacked portraits + eyebrow + linked names, or portrait + name
|
|
208
|
+
+ "Watch the video ↗"; the person chip (portrait, name, role · registration,
|
|
209
|
+
arrow bubble when it links to the person's card). No photo → initials bubble.
|
|
210
|
+
- **Breadcrumb**: the article trail "← Section › Parent" (current page not listed).
|
|
211
|
+
- **FactStrip**: the three facts under the hero of novo.creeta — icon bubble +
|
|
212
|
+
two lines of equal weight per fact, thin dividers between columns (rows on
|
|
213
|
+
phones); the separator between the lines is read by screen readers only.
|
|
214
|
+
- **InfoCard**: the white info cards of the clinic page (icon bubble, title,
|
|
215
|
+
lines/address, note, outbound links "Directions ↗ · Waze ↗").
|
|
216
|
+
- **MediaCard**: photo or muted clip behind a title, badge top-left, arrow button
|
|
217
|
+
top-right, whole card a link; Ken Burns zoom on hover (mouse only), clip plays
|
|
218
|
+
while visible or on hover; pause hooks exported for a page-level pause button.
|
|
219
|
+
The portrait variant ("Who created the method"): `eyebrow` + title + `linkLabel`
|
|
220
|
+
("See the team ↗") with `arrowButton={false}`.
|
|
221
|
+
- **VideoStoryCard**: the vertical Instagram-style video card (poster, play
|
|
222
|
+
button, duration badge; gradient grows and the lead appears on hover/focus;
|
|
223
|
+
tap plays with sound in place; optional muted hover preview). When the clip
|
|
224
|
+
ends the card returns to the poster with a "Watched" badge (check + word,
|
|
225
|
+
top-right): `watched` sets it from the site, `onWatched` fires once at the end,
|
|
226
|
+
`remember` keeps the set of watched clips in localStorage (by `src`). Exports
|
|
227
|
+
the page-level pause contract (`CARD_VIDEOS_PAUSE_EVENT` / `CARD_VIDEOS_PAUSED_ATTR`).
|
|
228
|
+
- **TestimonialCard**: clip with a play button on the left, quote + author +
|
|
229
|
+
outbound link on the right ("Famílias contam").
|
|
230
|
+
- **MapCard**: the Mapa of novo.creeta — a still preview fading into the card,
|
|
231
|
+
the address, "View the map" and the outbound "Directions" link; Google is only
|
|
232
|
+
called after the tap (no consent banner, lighter page). Open, the map fills
|
|
233
|
+
the card with the same close bar as the hero video.
|
|
234
|
+
- **CtaCard** / **VideoInviteCard**: the final call of a novo.creeta page — white
|
|
235
|
+
card (title, lead, CTA slot) beside a video card: muted loop with the speaker's
|
|
236
|
+
name over it, pause control, play button that plays the full video with sound.
|
|
237
|
+
- **Footer**: the site-footer of novo.creeta, by tokens — full width like the
|
|
238
|
+
Header, with the signed brand + sentence, "Pages" (title on the first line, links in two
|
|
239
|
+
columns), "Where we are" (address, hours, "Directions ↗ · Waze ↗"), an optional
|
|
240
|
+
notice band on `surface`, the legal line with the **CNPJ (always required in
|
|
241
|
+
the family)** plus the contact chips, and the languages as plain links.
|
|
242
|
+
`navHiddenFrom` keeps room at the bottom for the BottomNav.
|
|
243
|
+
|
|
244
|
+
## Playground (see the components)
|
|
245
|
+
|
|
246
|
+
`cd playground && npm install && npm run dev` → kitchen sink at http://localhost:5177 with the real components rendered (ttcab palette).
|
|
166
247
|
|
|
167
248
|
## Roadmap
|
|
168
249
|
|
|
169
|
-
- [x] v0.1 — tokens (preset +
|
|
170
|
-
- [x] Accordion (
|
|
171
|
-
- [x]
|
|
172
|
-
- [ ] **
|
|
173
|
-
**+
|
|
174
|
-
- [ ]
|
|
175
|
-
|
|
176
|
-
- [ ] **MediaCard** (
|
|
177
|
-
|
|
178
|
-
- [
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
- [
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
- [
|
|
189
|
-
|
|
190
|
-
- [
|
|
191
|
-
|
|
192
|
-
- [
|
|
193
|
-
|
|
194
|
-
- [ ] **OrganicFrame** (recorte de imagem orgânico, tipo blob) — registrado como **OPÇÃO**
|
|
195
|
-
(não default pra todos os sites). **Já existe no ttcab** → extrair como variante opcional.
|
|
196
|
-
- [x] Primitivos: Button (com variant link), Container, SectionKicker, Card
|
|
197
|
-
- [ ] Migrar ttcab e creeta para consumir o pacote + `npm publish`
|
|
250
|
+
- [x] v0.1 — tokens (preset + ttcab/creeta palettes) + preview
|
|
251
|
+
- [x] Accordion (1st component; injectable-deps pattern) — the Perguntas of novo.creeta: white card (`bg-card`), 22px radius, no border
|
|
252
|
+
- [x] Family typography (Quicksand + Figtree, free fonts only)
|
|
253
|
+
- [ ] **novo.creeta scales** (full reference, approved): **typography** (sizes/weights/line-height)
|
|
254
|
+
**+ spacing/rhythm** (section padding, gaps) **+ default border-radius** → become DS tokens
|
|
255
|
+
- [ ] **Icons**: package the lucide set used by the family, so components can
|
|
256
|
+
import/inject it (without each site depending on lucide directly)
|
|
257
|
+
- [ ] **MediaCard** (video/image behind + icon badge + title + lead + arrow;
|
|
258
|
+
**horizontal carousel on mobile** variant with progress bar + pause) — approved
|
|
259
|
+
- [x] **TestimonialCard** — from novo.creeta (10/29): clip + play on the left, quote/author/link on the right. The "video hero" and "video beside" variants from ttcab are still open
|
|
260
|
+
- [x] **VideoStoryCard** — from novo.creeta (10/29): vertical card, play + duration badge, lead on hover/focus, plays with sound in place; "Read the transcript" goes in `children` (Accordion)
|
|
261
|
+
- [x] **ProfessionalCard** (team) — from novo.creeta (10/29): small = PersonChip/Byline, big = the card with living portrait, presentation modal + captions. Original note: Two versions: **small** (photo + name, in a row)
|
|
262
|
+
and **big** (photo + name + role + CREFITO + "Trajetória"/"Orientações" accordions).
|
|
263
|
+
**Video** variant: "Apresentação" badge, hover plays the video, and a **modal** (video + bio).
|
|
264
|
+
**Data rule:** every professional MUST have a **CREFITO** attached (COFFITO requirement).
|
|
265
|
+
- [x] **Pill / PillList** — the Atalho of novo.creeta (white 44px pill, arrow bubble: ↓ anchor, ↗ other page, optional meta); wrapping row + "See all"
|
|
266
|
+
- [x] **AnchorNav** (floating capsule of anchors, scroll-spy + sliding indicator) — extracted from ttcab (10/29)
|
|
267
|
+
- [x] **Footer** · **PageVideoHero** (loop and player) · **Header** (dropdown, house) · **BottomNav** · **Curtain** · **Lockup** (5 arrangements) · **BreatheMark**
|
|
268
|
+
- [x] **Site brand marks** (`components/marks/`): real symbol + wordmark for TTCAB and Creeta (10/29)
|
|
269
|
+
- [ ] **ThreadRail** (side graphic device — a thread running down the left linking sections) — approved,
|
|
270
|
+
**already exists in ttcab** → extract.
|
|
271
|
+
- [ ] **OrganicFrame** (organic, blob-like image crop) — recorded as an **OPTION**
|
|
272
|
+
(not the default for every site). **Already exists in ttcab** → extract as an optional variant.
|
|
273
|
+
- [x] Primitives: Button (with link variant), Container, SectionKicker, Card
|
|
274
|
+
- [ ] Migrate ttcab and creeta to consume the package + `npm publish`
|
package/components/Accordion.tsx
CHANGED
|
@@ -10,20 +10,21 @@ export type AccordionItem = {
|
|
|
10
10
|
|
|
11
11
|
type Props = {
|
|
12
12
|
items: AccordionItem[];
|
|
13
|
-
/** id
|
|
13
|
+
/** id open initially (default: the first). null = all closed. */
|
|
14
14
|
defaultOpenId?: string | null;
|
|
15
|
-
/** true (
|
|
15
|
+
/** true (default): only one open at a time, as on creeta. */
|
|
16
16
|
single?: boolean;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Accordion / collapse
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* Accordion / collapse of the Cuidado Global family. Cards in `surface` with
|
|
21
|
+
* `line` border, `ink` title and a chevron that rotates on open. No app
|
|
22
|
+
* dependency: the icon is an inline SVG. Animates height with the grid-rows
|
|
23
|
+
* trick (0fr→1fr), without measuring anything in JS, and respects
|
|
24
|
+
* prefers-reduced-motion.
|
|
24
25
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
26
|
+
* Colors: `ink` text on `surface` (contrast of the role defined by the site).
|
|
27
|
+
* Consumes the tokens via Tailwind classes (surface/ink/line) — see theme.css.
|
|
27
28
|
*/
|
|
28
29
|
export function Accordion({ items, defaultOpenId, single = true }: Props) {
|
|
29
30
|
const initial =
|
|
@@ -50,7 +51,8 @@ export function Accordion({ items, defaultOpenId, single = true }: Props) {
|
|
|
50
51
|
return (
|
|
51
52
|
<div
|
|
52
53
|
key={it.id}
|
|
53
|
-
|
|
54
|
+
// the Perguntas of novo.creeta: a white card (bg-card), 22px radius, no border — it reads by tone over the page
|
|
55
|
+
className="overflow-hidden rounded-[22px] bg-card px-5 md:px-7"
|
|
54
56
|
>
|
|
55
57
|
<h3 className="m-0">
|
|
56
58
|
<button
|
|
@@ -59,13 +61,13 @@ export function Accordion({ items, defaultOpenId, single = true }: Props) {
|
|
|
59
61
|
aria-expanded={isOpen}
|
|
60
62
|
aria-controls={panelId}
|
|
61
63
|
onClick={() => toggle(it.id)}
|
|
62
|
-
className="flex w-full items-center justify-between gap-4
|
|
64
|
+
className="flex min-h-14 w-full items-center justify-between gap-4 py-1 text-left font-heading text-lg font-medium text-ink transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink"
|
|
63
65
|
>
|
|
64
66
|
<span>{it.title}</span>
|
|
65
67
|
<svg
|
|
66
68
|
aria-hidden="true"
|
|
67
69
|
viewBox="0 0 24 24"
|
|
68
|
-
className={`h-5 w-5 flex-none text-ink
|
|
70
|
+
className={`h-5 w-5 flex-none text-ink transition-transform duration-200 ease-[cubic-bezier(.2,.8,.2,1)] motion-reduce:transition-none ${
|
|
69
71
|
isOpen ? "rotate-180" : ""
|
|
70
72
|
}`}
|
|
71
73
|
fill="none"
|
|
@@ -78,19 +80,20 @@ export function Accordion({ items, defaultOpenId, single = true }: Props) {
|
|
|
78
80
|
</svg>
|
|
79
81
|
</button>
|
|
80
82
|
</h3>
|
|
81
|
-
{/* grid-rows 0fr→1fr:
|
|
83
|
+
{/* grid-rows 0fr→1fr: animates height without measuring in JS */}
|
|
82
84
|
<div
|
|
83
85
|
id={panelId}
|
|
84
86
|
role="region"
|
|
85
87
|
aria-labelledby={btnId}
|
|
86
|
-
// React 18
|
|
88
|
+
// React 18 does not know `inert` as a boolean: empty string turns it on, undefined turns it off (in 19 the boolean works the same way).
|
|
87
89
|
{...({ inert: isOpen ? undefined : "" } as Record<string, unknown>)}
|
|
88
90
|
className={`grid transition-[grid-template-rows] duration-300 ease-[cubic-bezier(.2,.8,.2,1)] motion-reduce:transition-none ${
|
|
89
91
|
isOpen ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
|
|
90
92
|
}`}
|
|
91
93
|
>
|
|
92
94
|
<div className="min-h-0 overflow-hidden">
|
|
93
|
-
|
|
95
|
+
{/* full-width item, reading-width body (max-w-2xl), as the Perguntas of novo.creeta */}
|
|
96
|
+
<div className="max-w-2xl pb-5 leading-relaxed text-ink/80">
|
|
94
97
|
{it.content}
|
|
95
98
|
</div>
|
|
96
99
|
</div>
|