@neo-design/foundations 1.0.0-rc.4

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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -0
  3. package/css/alert.css +133 -0
  4. package/css/aspect-ratio.css +32 -0
  5. package/css/badge.css +57 -0
  6. package/css/banner.css +220 -0
  7. package/css/button-icon.css +14 -0
  8. package/css/button.css +174 -0
  9. package/css/checkbox.css +124 -0
  10. package/css/chips-group.css +26 -0
  11. package/css/chips.css +88 -0
  12. package/css/combobox.css +229 -0
  13. package/css/empty-state.css +149 -0
  14. package/css/index.css +36 -0
  15. package/css/link.css +39 -0
  16. package/css/menu.css +112 -0
  17. package/css/modal-dialog.css +204 -0
  18. package/css/modal-fullscreen.css +145 -0
  19. package/css/radio-button.css +132 -0
  20. package/css/select.css +181 -0
  21. package/css/snackbar.css +85 -0
  22. package/css/spinner.css +21 -0
  23. package/css/tabs.css +126 -0
  24. package/css/tag.css +38 -0
  25. package/css/text-field.css +224 -0
  26. package/css/toggle.css +53 -0
  27. package/css/tooltip.css +32 -0
  28. package/icon-aliases.mjs +57 -0
  29. package/icons/README.md +84 -0
  30. package/icons/icons.css +16 -0
  31. package/icons/index.json +843 -0
  32. package/icons/sprite.mjs +2 -0
  33. package/package.json +44 -0
  34. package/tokens/_brand.mjs +3 -0
  35. package/tokens/_primitives.mjs +44 -0
  36. package/tokens/_util.mjs +12 -0
  37. package/tokens/api-js.mjs +11 -0
  38. package/tokens/breakpoints.mjs +8 -0
  39. package/tokens/color-semantic.mjs +47 -0
  40. package/tokens/color.mjs +58 -0
  41. package/tokens/component-tokens.mjs +24 -0
  42. package/tokens/container.mjs +8 -0
  43. package/tokens/iconSize.mjs +18 -0
  44. package/tokens/neo-color.css +97 -0
  45. package/tokens/neo-foundations.css +250 -0
  46. package/tokens/radius.mjs +18 -0
  47. package/tokens/shadows.mjs +14 -0
  48. package/tokens/spacing.mjs +21 -0
  49. package/tokens/stroke.mjs +15 -0
  50. package/tokens/typography.mjs +59 -0
package/css/toggle.css ADDED
@@ -0,0 +1,53 @@
1
+ /* packages/foundations/css/toggle.css — emitted without comments. The reasoning behind each decision lives in Neo's source repository. */
2
+
3
+ .toggle {
4
+ display: inline-flex;
5
+ align-items: center;
6
+ justify-content: center;
7
+ width: 48px;
8
+ height: 44px;
9
+ padding: 0;
10
+ border: none;
11
+ background: transparent;
12
+ cursor: pointer;
13
+ }
14
+
15
+ .toggle__track {
16
+ display: inline-flex;
17
+ align-items: center;
18
+ width: 48px;
19
+ height: 24px;
20
+ padding-inline: var(--neo-space-xs-rem);
21
+ border-radius: var(--neo-radius-pill);
22
+ background: var(--fill--base--strong);
23
+ transition: background 120ms ease-out;
24
+ }
25
+
26
+ .toggle__thumb {
27
+ width: 16px;
28
+ height: 16px;
29
+ border-radius: var(--neo-radius-pill);
30
+ background: var(--fill--base--default);
31
+ transition: transform 120ms ease-out;
32
+ }
33
+
34
+ .toggle[aria-checked="true"] .toggle__track { background: var(--fill--semantic--success--solid); }
35
+ .toggle[aria-checked="true"] .toggle__thumb { transform: translateX(var(--neo-switch-thumb-travel-rem)); }
36
+
37
+ .toggle:focus-visible { outline: none; }
38
+ .toggle:focus-visible .toggle__track {
39
+ box-shadow:
40
+ 0 0 0 var(--neo-stroke-focus-ring-width) var(--focus--gap--default),
41
+ 0 0 0 calc(2 * var(--neo-stroke-focus-ring-width)) var(--focus--ring--default);
42
+ }
43
+
44
+ .toggle:disabled { cursor: not-allowed; }
45
+ .toggle:disabled .toggle__track {
46
+ background: var(--fill--base--disabled);
47
+ border: var(--neo-stroke-xs) solid var(--border--base--disabled);
48
+ }
49
+ .toggle:disabled .toggle__thumb { background: var(--fill--base--medium); }
50
+
51
+ @media (prefers-reduced-motion: reduce) {
52
+ .toggle__track, .toggle__thumb { transition: none; }
53
+ }
@@ -0,0 +1,32 @@
1
+ /* packages/foundations/css/tooltip.css — emitted without comments. The reasoning behind each decision lives in Neo's source repository. */
2
+
3
+ .tooltip {
4
+ display: inline-block;
5
+ max-width: 40ch;
6
+ padding-inline: var(--neo-space-sm-rem);
7
+ padding-block: var(--neo-space-xs-rem);
8
+ border-radius: var(--neo-radius-xs);
9
+ background: var(--fill--semantic--info--solid);
10
+ color: var(--text--base--contrast);
11
+ font-size: var(--neo-type-body-md-regular-size-rem);
12
+ font-weight: var(--neo-type-body-md-regular-weight);
13
+ line-height: var(--neo-type-body-md-regular-line-height-rem);
14
+ position: relative;
15
+ }
16
+
17
+ .tooltip[hidden] { display: none; }
18
+
19
+ .tooltip::before {
20
+ content: none;
21
+ position: absolute;
22
+ border: 6px solid transparent;
23
+ }
24
+
25
+ .tooltip--top::before { content: ""; bottom: 100%; left: 50%; transform: translateX(-50%); border-bottom-color: var(--fill--semantic--info--solid); }
26
+ .tooltip--bottom::before { content: ""; top: 100%; left: 50%; transform: translateX(-50%); border-top-color: var(--fill--semantic--info--solid); }
27
+ .tooltip--left::before { content: ""; right: 100%; top: 50%; transform: translateY(-50%); border-right-color: var(--fill--semantic--info--solid); }
28
+ .tooltip--right::before { content: ""; left: 100%; top: 50%; transform: translateY(-50%); border-left-color: var(--fill--semantic--info--solid); }
29
+ .tooltip--top-start::before { content: ""; bottom: 100%; left: var(--neo-space-md-rem); border-bottom-color: var(--fill--semantic--info--solid); }
30
+ .tooltip--top-end::before { content: ""; bottom: 100%; right: var(--neo-space-md-rem); border-bottom-color: var(--fill--semantic--info--solid); }
31
+ .tooltip--bottom-start::before { content: ""; top: 100%; left: var(--neo-space-md-rem); border-top-color: var(--fill--semantic--info--solid); }
32
+ .tooltip--bottom-end::before { content: ""; top: 100%; right: var(--neo-space-md-rem); border-top-color: var(--fill--semantic--info--solid); }
@@ -0,0 +1,57 @@
1
+ // packages/foundations/icon-aliases.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
2
+
3
+ export const approved = true;
4
+ export const appliedInFigma = '2026-09-06';
5
+ export const mirroredInCode = true;
6
+ export const mirroredOn = '2026-09-06';
7
+
8
+ export const SYSTEM = {
9
+ advertencia: 'warning', agregar: 'add', 'agregar-circulo': 'add-circle', alarma: 'alarm', ayuda: 'help',
10
+ 'bandeja-entrada': 'inbox', 'bandeja-salida': 'outbox', bano: 'restroom', 'bano-mujeres': 'restroom-women',
11
+ billetera: 'wallet', bolsa: 'bag', bombilla: 'lightbulb', brujula: 'compass', buscar: 'search',
12
+ 'buscar-circulo': 'search-circle', 'buzon-voz': 'voicemail', 'buzon-voz-silencio': 'voicemail-off',
13
+ calendario: 'calendar', 'calendario-agregar': 'calendar-add', campana: 'bell', 'campana-agregar': 'bell-add',
14
+ 'campana-bloquear': 'bell-block', 'campana-silencio': 'bell-off', 'campana-timbrar': 'bell-ring', candado: 'lock',
15
+ carrito: 'cart', 'carrito-agregar': 'cart-add', 'carrito-verificar': 'cart-check', celular: 'mobile',
16
+ cerrar: 'close', 'cerrar-sesion': 'logout', 'chat-burbujas': 'chat', 'chat-escribiendo': 'chat-typing',
17
+ 'chevron-abajo': 'chevron-down', 'chevron-arriba': 'chevron-up', 'chevron-derecha': 'chevron-right',
18
+ 'chevron-izquierda': 'chevron-left', configuracion: 'settings', copiar: 'copy', corazon: 'heart',
19
+ 'corazon-desactivar': 'heart-off', correo: 'mail', 'correo-enviar': 'mail-send', 'derivacion-out': 'referral-out',
20
+ descargar: 'download', diccionario: 'dictionary', editar: 'edit', enlace: 'link', 'enlace-roto': 'link-broken',
21
+ error: 'error', escaner: 'scanner', escudo: 'shield', 'escudo-verificar': 'shield-check', estrella: 'star',
22
+ 'flecha-abajo': 'arrow-down', 'flecha-arriba': 'arrow-up', 'flecha-derecha': 'arrow-right',
23
+ 'flecha-izquierda': 'arrow-left', 'grafico-baja': 'chart-down', imagen: 'image', informacion: 'info',
24
+ inicio: 'home', kebab: 'kebab', lista: 'list', 'lista-tareas': 'checklist', 'llamada-entrante': 'call-incoming',
25
+ 'llamada-entrante-alt': 'call-incoming-alt', mapa: 'map', marcador: 'bookmark', 'media-anterior': 'media-previous',
26
+ 'media-avanzar': 'media-forward', 'media-pausa': 'media-pause', 'media-reproducir': 'media-play',
27
+ 'media-retroceder': 'media-rewind', 'media-siguiente': 'media-next', megafono: 'megaphone', menos: 'minus',
28
+ 'menu-puntos': 'menu-dots', moneda: 'coin', 'nav-derecha': 'nav-right', 'nav-izquierda': 'nav-left',
29
+ navegar: 'navigate', 'navegar-desactivar': 'navigate-off', nube: 'cloud', 'nube-actualizar': 'cloud-refresh',
30
+ 'nube-agregar': 'cloud-add', 'nube-alerta': 'cloud-alert', 'nube-bloquear': 'cloud-block',
31
+ 'nube-compartir': 'cloud-share', 'nube-transferir': 'cloud-transfer', 'nube-verificar': 'cloud-check',
32
+ 'nube-video': 'cloud-video', ojo: 'eye', 'ojo-oculto': 'eye-off', 'papelera-reciclaje': 'trash',
33
+ 'portapapeles-agregar': 'clipboard-add', 'portapapeles-eliminar': 'clipboard-remove',
34
+ 'portapapeles-verificar': 'clipboard-check', regalo: 'gift', 'reloj-circulo': 'clock', senal: 'signal',
35
+ 'silla-ruedas': 'wheelchair', 'sincronizar-alerta': 'sync-alert', soporte: 'support', 'tarjeta-credito': 'credit-card',
36
+ telefono: 'phone', ubicacion: 'location', 'usuario-agregar': 'user-add', 'usuario-circulo': 'user-circle',
37
+ 'usuario-id': 'user-id', 'usuario-perfil': 'user-profile', 'usuario-proteger': 'user-shield',
38
+ 'usuario-verificar': 'user-check', verificar: 'check', 'verificar-cuadro': 'check-box', 'volumen-alto': 'volume-high',
39
+ 'volumen-apagar': 'volume-off', 'volumen-bajar': 'volume-down', 'volumen-bajo': 'volume-low',
40
+ 'volumen-mudo': 'volume-mute', wifi: 'wifi', 'wifi-desactivar': 'wifi-off',
41
+ };
42
+
43
+ export const BRAND = {
44
+ ambulancia: 'ambulance', bienestar: 'wellness', brillos: 'sparkles', 'call-center': 'call-center', capsula: 'capsule',
45
+ chequeo: 'checkup', contacto: 'contact', conversacion: 'conversation', cruz: 'cross', desktop: 'desktop',
46
+ diente: 'tooth', dinero: 'money', 'donar-sangre': 'blood-donation', enfermero: 'nurse', estetoscopio: 'stethoscope',
47
+ garantia: 'warranty', 'gorro-enfermero': 'nurse-cap', 'gota-sangre': 'blood-drop', 'historial-medico': 'medical-record',
48
+ hospital: 'hospital', hospitalsymbol: 'hospital-symbol', 'maletin-medico': 'medical-bag', mascarilla: 'mask',
49
+ oido: 'ear', ojo: 'eye', pelota: 'ball', 'ritmo-cardiaco': 'heart-rate', salud: 'health', seguro: 'insurance',
50
+ 'silla-ruedas': 'wheelchair', 'tarjeta-credito': 'credit-card', telemedicina: 'telemedicine',
51
+ };
52
+
53
+
54
+ export const ALIASES = Object.fromEntries([
55
+ ...Object.entries(SYSTEM).map(([v, n]) => [`system-${v}`, `system-${n}`]),
56
+ ...Object.entries(BRAND).map(([v, n]) => [`brand-${v}`, `brand-${n}`]),
57
+ ].filter(([oldName, newName]) => oldName !== newName));
@@ -0,0 +1,84 @@
1
+ # Neo — Icons
2
+
3
+ 167 icons in 3 families —`system`, `semantic` and `brand`, all three compared against the library—, generated from the Figma **Icons** library, which is the source of truth.
4
+ They are not edited by hand: they are regenerated from there.
5
+
6
+ **The set defines no colors of its own: it consumes the color variables of the token system.**
7
+
8
+ | Family | How many | What it is | Color |
9
+ |---|---|---|---|
10
+ | `system` | 125 | **interface** icons: actions, navigation, neutral states | `icon/base/default` by default — **can vary** within the `icon/base/*` base |
11
+ | `semantic` | 10 (5 + 5 `-inverse`) | **feedback** icons: info, alert, success, warning, error | already defined, `icon/semantic/*`. **Not modified** |
12
+ | `brand` | 32 | **brand identity** icons | its own duality, already defined. **Not modified** |
13
+
14
+ > The error state ships **two drawings**: `semantic-error` ("✕") and `semantic-alert` ("!"). Both are
15
+ > painted by `icon/semantic/error`. In the Figma library the first one is named `danger`; the package
16
+ > renames it to `error`, so `danger` is not an id you can use here.
17
+
18
+ ## Usage
19
+
20
+ It needs the CSS vars on `:root` — the theme's `<CssBaseline/>` injects them, or the two CSS files in `tokens/` are loaded.
21
+
22
+ The sprite is injected once into the document, and each icon is then requested by its id
23
+ (`<family>-<name>`):
24
+
25
+ ```html
26
+ <svg class="icon"><use href="#system-close"/></svg>
27
+ <svg class="icon icon--lg"><use href="#brand-health"/></svg>
28
+ ```
29
+
30
+ ```css
31
+ /* icons.css brings the base class and the sizes */
32
+ .icon /* 24px · color icon/base/default */
33
+ .icon--xs|sm|md|lg|xl
34
+ .icon--secondary|disabled|inverse
35
+ ```
36
+
37
+ ### Which can change color and which cannot
38
+
39
+ **Only the `system` ones.** They come in `icon/base/default` and can take another token **from the same
40
+ base family** as the context requires — `secondary` for lower hierarchy, `disabled` in a
41
+ disabled control, `contrast` on a dark background. The handoff says which one goes on each screen.
42
+
43
+ Since they are exported with `fill="currentColor"`, it is applied by setting `color` on the container:
44
+
45
+ ```css
46
+ .mi-componente__icono { color: var(--icon--base--secondary); }
47
+ ```
48
+
49
+ The `.icon--secondary|disabled|inverse` classes cover those cases.
50
+
51
+ > **`semantic` and `brand` are not recolored.** They carry their color inside on purpose: an error icon
52
+ > is red wherever it is, and the brand ones keep their duality. Neither `color` nor `currentColor`
53
+ > affects them — and changing them would break the meaning the icon conveys.
54
+
55
+ > A `system` icon **does not take semantic colors**: if something has to convey error or success, the
56
+ > right icon is the one from the `semantic` family, not a `system` painted red.
57
+
58
+ `index.json` lists the 167 icons by family, with their `viewBox` and their color type — useful to
59
+ populate a picker or validate names at build time.
60
+
61
+ ## How they are consumed from the package
62
+
63
+ ```js
64
+ import '@neo-design/foundations/icons.css';
65
+ import { sprite } from '@neo-design/foundations/sprite';
66
+ ```
67
+
68
+ **The sprite is injected into the document and each icon is requested by id:**
69
+
70
+ ```html
71
+ <svg class="icon"><use href="#system-add" /></svg>
72
+ ```
73
+
74
+ **It is not referenced as a file** —`<use href="icons.svg#id">`— because the browser blocks it by
75
+ CORS. Injecting the sprite once, as high up the tree as possible, avoids the whole case. That is why
76
+ it ships as a string module and not as a file: `import { sprite }` resolves in any bundler and in
77
+ Node, with nothing to configure.
78
+
79
+ And the three families behave differently: **`system`** inherits the context's color (or the
80
+ `.icon` class sets it), **`semantic`** already carries it bound to its token and needs no class, and **`brand`** is
81
+ its own multicolor — it only accepts a size change. The integration guide repeats it with examples.
82
+
83
+ > **Only `icons.css`, `sprite` and `icons.json` are exported**, not the loose SVG files nor the
84
+ > sprite as a file: an `exports` map is an allowlist, and consumption is by sprite, not by file.
@@ -0,0 +1,16 @@
1
+ /* packages/foundations/icons/icons.css — emitted without comments. The reasoning behind each decision lives in Neo's source repository. */
2
+
3
+ .icon { display: inline-flex; flex-shrink: 0; width: var(--neo-icon-size-md); height: var(--neo-icon-size-md); }
4
+ .icon--xs { width: var(--neo-icon-size-xs); height: var(--neo-icon-size-xs); }
5
+ .icon--sm { width: var(--neo-icon-size-sm); height: var(--neo-icon-size-sm); }
6
+ .icon--md { width: var(--neo-icon-size-md); height: var(--neo-icon-size-md); }
7
+ .icon--lg { width: var(--neo-icon-size-lg); height: var(--neo-icon-size-lg); }
8
+ .icon--xl { width: var(--neo-icon-size-xl); height: var(--neo-icon-size-xl); }
9
+ .icon--2xl { width: var(--neo-icon-size-2xl); height: var(--neo-icon-size-2xl); }
10
+ .icon--3xl { width: var(--neo-icon-size-3xl); height: var(--neo-icon-size-3xl); }
11
+ .icon--4xl { width: var(--neo-icon-size-4xl); height: var(--neo-icon-size-4xl); }
12
+
13
+ .icon { color: var(--icon--base--default); }
14
+ .icon--secondary { color: var(--icon--base--secondary); }
15
+ .icon--disabled { color: var(--icon--base--disabled); }
16
+ .icon--inverse { color: var(--icon--base--contrast); }