|
|
|
|
|
/**
|
|
|
|
|
|
* `<palabras>` — editor-specific styles.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Layout shell is composed from our components (Flex). This file styles what
|
|
|
|
|
|
* those don't: the editing surface, the active-block selection marker, and the
|
|
|
|
|
|
* FLOATING inspector panel (its own overlay surface — deliberately distinct from
|
|
|
|
|
|
* the docked Words inspector). Content reading styles stay minimal — `scheme-style`
|
|
|
|
|
|
* will own the per-element look next.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
[data-palabras] {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-shell] {
|
|
|
|
|
|
inline-size: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The breadcrumb wrapper only delegates clicks — transparent to layout. */
|
|
|
|
|
|
[data-palabras-breadcrumb-wrap] {
|
|
|
|
|
|
display: contents;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ── Stage: document full-width, panel floats over the frame's top-right ──── */
|
|
|
|
|
|
[data-palabras-stage] {
|
|
|
|
|
|
/* not positioned: the seam line (painted by Words' block-drag controller) is
|
|
|
|
|
|
calculated frame-relative; the inspector panel becomes frame-anchored too
|
|
|
|
|
|
via the rules below. */
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-doc-card] {
|
|
|
|
|
|
min-block-size: 20rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Make the Words frame the positioned ancestor for the drop-line seam (and the
|
|
|
|
|
|
inspector panel below). The Words.Provider stamps `data-words` on the SAME
|
|
|
|
|
|
div palabras stamps `data-palabras`, so the compound attribute selector
|
|
|
|
|
|
targets this exact element only; standalone <Words> is untouched. */
|
|
|
|
|
|
[data-palabras][data-words] {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-doc] {
|
|
|
|
|
|
/* Two real columns: the grip RAIL (its own div with width) + the canvas. */
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: stretch;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Left RAIL — a real column with its OWN width (not padding). Holds the block
|
|
|
|
|
|
grip; `position: relative` so the grip slot positions against it. Same width
|
|
|
|
|
|
on mobile + desktop (option A). */
|
|
|
|
|
|
[data-palabras-rail] {
|
|
|
|
|
|
flex: 0 0 var(--palabras-rail, 32px);
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
/* Bullet-proof containing block for the absolutely-positioned grip slot. If
|
|
|
|
|
|
`position: relative` alone ever fails to register the rail as the slot's
|
|
|
|
|
|
offsetParent, the slot jumps past DOC + CARD (both static — they can't be CBs
|
|
|
|
|
|
without breaking the Words drop-line seam's frame anchor) straight to the outer
|
|
|
|
|
|
frame, landing the grip ~25px LEFT at the card edge. A transform establishes
|
|
|
|
|
|
the containing block via a second, independent mechanism so it can't fall through. */
|
|
|
|
|
|
transform: translateZ(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Canvas — the editing column. `min-inline-size: 0` lets it shrink within the
|
|
|
|
|
|
flex row instead of overflowing. NOT positioned: the drop-line seam painted by
|
|
|
|
|
|
Words' block-drag controller is calculated frame-relative ([data-words]); any
|
|
|
|
|
|
positioned ancestor between the seam and the frame breaks the anchor. */
|
|
|
|
|
|
[data-palabras-canvas] {
|
|
|
|
|
|
flex: 1 1 auto;
|
|
|
|
|
|
min-inline-size: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Block-grip SLOT — owns the gutter placement. The grip sits at the visited
|
|
|
|
|
|
block's mid-height (PalabrasHandle sets inline `top` = block centre) and
|
|
|
|
|
|
GLIDES between blocks via the `top` transition. The vertical centring
|
|
|
|
|
|
(translateY(-50%)) lives on the slot, NOT the Button, so the Button's
|
|
|
|
|
|
`:active { translateY(1px) }` press transform never fights it. Scoped under
|
|
|
|
|
|
[data-palabras-doc] (0,2,0) to beat the Button recipe's `position`. */
|
|
|
|
|
|
[data-palabras-doc] [data-palabras-handle-slot] {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
/* `left` is set INLINE per-block by PalabrasHandle: 0 for a top-level block
|
|
|
|
|
|
(flush in the rail), or the block's own left edge for a NESTED block (the
|
|
|
|
|
|
handle floating out of the rail into the canvas — the rail has no overflow
|
|
|
|
|
|
clip). Only `top` glides; `left` snaps so a rail↔inline jump isn't a fly. */
|
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
|
z-index: 5;
|
|
|
|
|
|
transition: top var(--duration-fast, 0.12s) var(--ease-default, ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Action-menu entrance — pin ONE animation. The menu gets TWO competing entrance
|
|
|
|
|
|
animations on open: the sema/motion `present-rise` (applied WHILE the
|
|
|
|
|
|
`data-event` hold is up, ~240ms, because the `open` event is family `emerge`)
|
|
|
|
|
|
and the dropdown recipe's `dropdown-menu-enter` (on `data-state=open`). When
|
|
|
|
|
|
the hold ends the `animation-name` switches → the browser RESTARTS the
|
|
|
|
|
|
animation, snapping opacity back to 0 for a frame. This menu is rendered
|
|
|
|
|
|
in-place (position:fixed but DOM-nested in the rail, NOT portaled), so that
|
|
|
|
|
|
transparent frame reveals the editor blocks behind it — the "block background
|
|
|
|
|
|
flashing over the menu" flicker. Pin the recipe animation with `!important`
|
|
|
|
|
|
so the name never switches and opacity never resets to 0. */
|
|
|
|
|
|
[data-palabras-handle-menu][data-state='open'] {
|
|
|
|
|
|
animation: dropdown-menu-enter var(--duration-fast, 0.12s) var(--ease-out, ease) !important;
|
|
|
|
|
|
}
|
palabras: menú de inserción (tipos+iconos), columnas visibles y "+" por columna
Insertar bloque -> rejilla de tipos:
- palabras-block-types.ts (nuevo): lista curada de 10 (Párrafo/Encabezado/Imagen/
Lista/Separador/Cita/Código/Callout/Columnas/Tabla; headings y listas
colapsados; imagen incluida) con icono explícito por tipo (los definió el
usuario). Sale del registro del motor por id, sin duplicar create().
blockToInsert rellena placeholder en heading/list; imagen via pickImageFile.
- Iconos en el menú de acciones (Settings2/LayersPlus/ArrowBigUp/ArrowBigDown/
Trash2).
- Menú PORTALIZADO (DropdownMenu.Portal): el slot del grip tiene transform, que
lo hace bloque contenedor de los position:fixed -> la colisión de floating-ui
se desajustaba y el submenú se volteaba a la izquierda. Portalizar lo saca del
slot transformado.
Columnas (palabras.css, espejo de Words words.css):
- Un bloque de columnas vacío se renderizaba con ALTURA 0 (palabras no carga
words.css), y de ahí salían 4 bugs de la MISMA raíz: invisible, grip "se
vuelve loco" (las bandas Voronoi-on-Y de blockAtY se degeneran con altura 0),
no se podía seleccionar (0px sin área clicable) y el insert caía off-by-one.
Fix: flex-row + min-block-size 3em + borde dashed por columna (Words ya lo
documentaba: "without this, an empty columns block collapses to 0 height").
"+" por columna (palabras-column-inserter.svelte nuevo):
- Overlay "+" sobre cada columna VACÍA -> la misma rejilla (sin Columnas) ->
insertBlockInColumn(columnsIdx, colIdx, block) (comando del motor, como el
inspector de Words). Posicionado frame-relative, menú portalizado,
data-words-external-tool. El "+" desaparece al llenarse la columna.
Deuda anotada en continuar-palabras.md (decisión del usuario: al cerrar el
componente): labels -> langs, morfo de palabras. Pendiente abierto: grip "fuera
de la card" (el offsetParent del slot cae al marco exterior).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
/* Every menu item (main actions + insert tiles + the submenu trigger) is an
|
|
|
|
|
|
icon + label row. The label grows so any trailing chevron (on the "Insertar
|
|
|
|
|
|
bloque" sub-trigger) is pushed to the right edge. */
|
|
|
|
|
|
[data-palabras-handle-menu] [role='menuitem'] {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-handle-menu] [role='menuitem'] > span {
|
|
|
|
|
|
flex: 1 1 auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-handle-menu] [role='menuitem'] svg {
|
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
|
inline-size: 1rem;
|
|
|
|
|
|
block-size: 1rem;
|
|
|
|
|
|
opacity: 0.85;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* "Insertar bloque" submenu — a compact 2-column grid of type tiles. Keyboard
|
|
|
|
|
|
arrow-nav stays vertical over DOM order (mouse-first for now). */
|
|
|
|
|
|
[data-palabras-insert-grid] {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(2, minmax(7.5rem, 1fr));
|
|
|
|
|
|
gap: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Unified round tool button — the block-action grip (⋮) AND the column "+"
|
|
|
|
|
|
inserter share ONE elevated, bordered, fully-round chrome (overriding the ghost
|
|
|
|
|
|
Button's transparent look). Scoped under [data-palabras-doc] (0,2,0) so it beats
|
|
|
|
|
|
the Button recipe. */
|
|
|
|
|
|
[data-palabras-doc] [data-palabras-handle],
|
|
|
|
|
|
[data-palabras-canvas] [data-palabras-column-inserter] [data-palabras-column-inserter-button] {
|
|
|
|
|
|
background: var(--color-surface-default);
|
|
|
|
|
|
border: 1px solid var(--color-neutral-border);
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
box-shadow:
|
|
|
|
|
|
0 4px 12px -2px rgb(0 0 0 / 0.22),
|
|
|
|
|
|
0 2px 4px -1px rgb(0 0 0 / 0.16);
|
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Hover — the arm carries `[data-button]` + `:not([data-disabled])` so it (0,5,0)
|
|
|
|
|
|
beats the eidos ghost-Button hover `[data-button]:hover:not()…` (0,4,0), which
|
|
|
|
|
|
for ghost forces bg + border to transparent. `background` is re-asserted because
|
|
|
|
|
|
the ghost hover would otherwise blank the round chip's surface. */
|
|
|
|
|
|
[data-palabras-doc] [data-button][data-palabras-handle]:hover:not([data-disabled]),
|
|
|
|
|
|
[data-palabras-canvas]
|
|
|
|
|
|
[data-palabras-column-inserter]
|
|
|
|
|
|
[data-button][data-palabras-column-inserter-button]:hover:not([data-disabled]) {
|
|
|
|
|
|
background: var(--color-surface-default);
|
|
|
|
|
|
border-color: var(--color-content-primary);
|
|
|
|
|
|
color: var(--color-content-primary);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The grip is also a drag source. */
|
|
|
|
|
|
[data-palabras-doc] [data-palabras-handle] {
|
|
|
|
|
|
cursor: grab;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-doc] [data-palabras-handle]:active {
|
|
|
|
|
|
cursor: grabbing;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* A block NESTED inside a column gets a slightly smaller grip than the
|
|
|
|
|
|
rail/top-level one (`scale` is independent of the recipe's `:active` transform,
|
|
|
|
|
|
so the press nudge still composes). */
|
|
|
|
|
|
[data-palabras-doc] [data-palabras-handle][data-nested] {
|
|
|
|
|
|
scale: 0.8;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-doc] [data-words-content] {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Drop indicator painted by Words' block-drag controller while a block is being
|
|
|
|
|
|
dragged by its grip. The controller positions it frame-relative ([data-words])
|
|
|
|
|
|
at the nearest inter-block seam; the bar spans the target block's width. We
|
|
|
|
|
|
restyle here (Words' own recipe is not loaded under palabras). */
|
|
|
|
|
|
[data-palabras-canvas] [data-words-block-drop-line] {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
z-index: 5;
|
|
|
|
|
|
block-size: 2px;
|
|
|
|
|
|
margin-block-start: -1px;
|
|
|
|
|
|
border-radius: 1px;
|
|
|
|
|
|
background: var(--color-primary-solid);
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Kill the archetype focus ring (box-shadow) on the editing surface — the caret
|
|
|
|
|
|
is the focus indicator; otherwise the whole document gets boxed in violet. */
|
|
|
|
|
|
[data-palabras-doc] [data-words-content][data-archetype]:focus-visible {
|
|
|
|
|
|
box-shadow: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-doc] [data-words-content] :where(h1, h2, h3, p) {
|
|
|
|
|
|
margin-block: 0 0.6em;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Selection marker for the active/selected block — dotted (NOT the solid focus
|
|
|
|
|
|
ring, which is suppressed above). Stamped by WordsActivate on the active
|
|
|
|
|
|
block element. */
|
|
|
|
|
|
[data-palabras-doc] [data-words-active] {
|
|
|
|
|
|
outline: 1.5px dashed var(--color-primary-border);
|
|
|
|
|
|
outline-offset: 6px;
|
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* A block nested inside a column (or a table cell) must keep its selection ring
|
|
|
|
|
|
WITHIN the container's own dashed border. The top-level 6px offset would spill
|
|
|
|
|
|
past it; a tighter 3px offset fits inside the container's inner padding
|
|
|
|
|
|
(`--space-2`), so the ring reads as "contained by" the structure, not bleeding
|
|
|
|
|
|
over it. */
|
|
|
|
|
|
[data-palabras-doc] :is([data-words-column], [data-words-node='table-cell']) [data-words-active] {
|
|
|
|
|
|
outline-offset: 3px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
palabras: menú de inserción (tipos+iconos), columnas visibles y "+" por columna
Insertar bloque -> rejilla de tipos:
- palabras-block-types.ts (nuevo): lista curada de 10 (Párrafo/Encabezado/Imagen/
Lista/Separador/Cita/Código/Callout/Columnas/Tabla; headings y listas
colapsados; imagen incluida) con icono explícito por tipo (los definió el
usuario). Sale del registro del motor por id, sin duplicar create().
blockToInsert rellena placeholder en heading/list; imagen via pickImageFile.
- Iconos en el menú de acciones (Settings2/LayersPlus/ArrowBigUp/ArrowBigDown/
Trash2).
- Menú PORTALIZADO (DropdownMenu.Portal): el slot del grip tiene transform, que
lo hace bloque contenedor de los position:fixed -> la colisión de floating-ui
se desajustaba y el submenú se volteaba a la izquierda. Portalizar lo saca del
slot transformado.
Columnas (palabras.css, espejo de Words words.css):
- Un bloque de columnas vacío se renderizaba con ALTURA 0 (palabras no carga
words.css), y de ahí salían 4 bugs de la MISMA raíz: invisible, grip "se
vuelve loco" (las bandas Voronoi-on-Y de blockAtY se degeneran con altura 0),
no se podía seleccionar (0px sin área clicable) y el insert caía off-by-one.
Fix: flex-row + min-block-size 3em + borde dashed por columna (Words ya lo
documentaba: "without this, an empty columns block collapses to 0 height").
"+" por columna (palabras-column-inserter.svelte nuevo):
- Overlay "+" sobre cada columna VACÍA -> la misma rejilla (sin Columnas) ->
insertBlockInColumn(columnsIdx, colIdx, block) (comando del motor, como el
inspector de Words). Posicionado frame-relative, menú portalizado,
data-words-external-tool. El "+" desaparece al llenarse la columna.
Deuda anotada en continuar-palabras.md (decisión del usuario: al cerrar el
componente): labels -> langs, morfo de palabras. Pendiente abierto: grip "fuera
de la card" (el offsetParent del slot cae al marco exterior).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
/* Columns block — flex layout, MIRRORED FROM Words' `words.css` (palabras does
|
|
|
|
|
|
NOT load Words' recipe, so without this the block gets no layout or height).
|
|
|
|
|
|
An empty columns block otherwise collapses to 0px: invisible AND it breaks the
|
|
|
|
|
|
grip — a zero-height block makes the rail's vertical block-targeting bands
|
|
|
|
|
|
(`blockAtY`, Voronoi-on-Y) DEGENERATE, so the grip jumps and the block can't
|
|
|
|
|
|
be selected or targeted, and inserts land off-by-one. `min-block-size` + the
|
|
|
|
|
|
dashed per-column outline give it real, discoverable, clickable structure. */
|
|
|
|
|
|
[data-palabras-doc] [data-words-block='columns'] {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
|
/* Wide enough for a block's inline grip handle to sit in the inter-column
|
|
|
|
|
|
gutter WITHOUT overlapping the previous column (an overlap re-anchors the
|
|
|
|
|
|
grip on hover). Tied to HANDLE_REACH in palabras-handle.svelte. */
|
|
|
|
|
|
gap: 2.5rem;
|
palabras: menú de inserción (tipos+iconos), columnas visibles y "+" por columna
Insertar bloque -> rejilla de tipos:
- palabras-block-types.ts (nuevo): lista curada de 10 (Párrafo/Encabezado/Imagen/
Lista/Separador/Cita/Código/Callout/Columnas/Tabla; headings y listas
colapsados; imagen incluida) con icono explícito por tipo (los definió el
usuario). Sale del registro del motor por id, sin duplicar create().
blockToInsert rellena placeholder en heading/list; imagen via pickImageFile.
- Iconos en el menú de acciones (Settings2/LayersPlus/ArrowBigUp/ArrowBigDown/
Trash2).
- Menú PORTALIZADO (DropdownMenu.Portal): el slot del grip tiene transform, que
lo hace bloque contenedor de los position:fixed -> la colisión de floating-ui
se desajustaba y el submenú se volteaba a la izquierda. Portalizar lo saca del
slot transformado.
Columnas (palabras.css, espejo de Words words.css):
- Un bloque de columnas vacío se renderizaba con ALTURA 0 (palabras no carga
words.css), y de ahí salían 4 bugs de la MISMA raíz: invisible, grip "se
vuelve loco" (las bandas Voronoi-on-Y de blockAtY se degeneran con altura 0),
no se podía seleccionar (0px sin área clicable) y el insert caía off-by-one.
Fix: flex-row + min-block-size 3em + borde dashed por columna (Words ya lo
documentaba: "without this, an empty columns block collapses to 0 height").
"+" por columna (palabras-column-inserter.svelte nuevo):
- Overlay "+" sobre cada columna VACÍA -> la misma rejilla (sin Columnas) ->
insertBlockInColumn(columnsIdx, colIdx, block) (comando del motor, como el
inspector de Words). Posicionado frame-relative, menú portalizado,
data-words-external-tool. El "+" desaparece al llenarse la columna.
Deuda anotada en continuar-palabras.md (decisión del usuario: al cerrar el
componente): labels -> langs, morfo de palabras. Pendiente abierto: grip "fuera
de la card" (el offsetParent del slot cae al marco exterior).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
margin-block: var(--space-3) 0;
|
|
|
|
|
|
align-items: stretch;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-doc] [data-words-block='columns'] [data-words-column] {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
min-inline-size: 0;
|
|
|
|
|
|
min-block-size: 3em;
|
|
|
|
|
|
/* Inner padding so a nested block is NOT flush with the column border. This
|
|
|
|
|
|
gives the active-block selection ring (an `outline` drawn OUTSIDE the block
|
|
|
|
|
|
box) room to sit INSIDE the column instead of spilling past its dashed
|
|
|
|
|
|
border. Must be >= the ring's reach (offset + width) — see the nested
|
|
|
|
|
|
`[data-words-active]` rule below. */
|
|
|
|
|
|
padding: var(--space-2);
|
palabras: menú de inserción (tipos+iconos), columnas visibles y "+" por columna
Insertar bloque -> rejilla de tipos:
- palabras-block-types.ts (nuevo): lista curada de 10 (Párrafo/Encabezado/Imagen/
Lista/Separador/Cita/Código/Callout/Columnas/Tabla; headings y listas
colapsados; imagen incluida) con icono explícito por tipo (los definió el
usuario). Sale del registro del motor por id, sin duplicar create().
blockToInsert rellena placeholder en heading/list; imagen via pickImageFile.
- Iconos en el menú de acciones (Settings2/LayersPlus/ArrowBigUp/ArrowBigDown/
Trash2).
- Menú PORTALIZADO (DropdownMenu.Portal): el slot del grip tiene transform, que
lo hace bloque contenedor de los position:fixed -> la colisión de floating-ui
se desajustaba y el submenú se volteaba a la izquierda. Portalizar lo saca del
slot transformado.
Columnas (palabras.css, espejo de Words words.css):
- Un bloque de columnas vacío se renderizaba con ALTURA 0 (palabras no carga
words.css), y de ahí salían 4 bugs de la MISMA raíz: invisible, grip "se
vuelve loco" (las bandas Voronoi-on-Y de blockAtY se degeneran con altura 0),
no se podía seleccionar (0px sin área clicable) y el insert caía off-by-one.
Fix: flex-row + min-block-size 3em + borde dashed por columna (Words ya lo
documentaba: "without this, an empty columns block collapses to 0 height").
"+" por columna (palabras-column-inserter.svelte nuevo):
- Overlay "+" sobre cada columna VACÍA -> la misma rejilla (sin Columnas) ->
insertBlockInColumn(columnsIdx, colIdx, block) (comando del motor, como el
inspector de Words). Posicionado frame-relative, menú portalizado,
data-words-external-tool. El "+" desaparece al llenarse la columna.
Deuda anotada en continuar-palabras.md (decisión del usuario: al cerrar el
componente): labels -> langs, morfo de palabras. Pendiente abierto: grip "fuera
de la card" (el offsetParent del slot cae al marco exterior).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
border: 1px dashed var(--color-neutral-border);
|
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
|
transition: border-color var(--duration-fast, 0.12s) var(--ease-default, ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-doc] [data-words-block='columns'] [data-words-column]:hover,
|
|
|
|
|
|
[data-palabras-doc] [data-words-block='columns'] [data-words-column]:focus-within {
|
|
|
|
|
|
border-color: var(--color-content-primary);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Reset the universal block-rhythm margin on each column's first nested block. */
|
|
|
|
|
|
[data-palabras-doc] [data-words-block='columns'] [data-words-column] > *:first-child {
|
|
|
|
|
|
margin-block-start: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Per-column "+" inserter — an overlay centred over each EMPTY column. Absolute
|
|
|
|
|
|
so it anchors to the FRAME (not the canvas — a positioned canvas breaks the
|
|
|
|
|
|
drop-line seam's frame-relative anchor); the inline left/top are
|
|
|
|
|
|
frame-relative. The menu is portaled, so the centring transform here doesn't
|
|
|
|
|
|
become the menu's containing block. */
|
|
|
|
|
|
[data-palabras-canvas] [data-palabras-column-inserter] {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
transform: translate(-50%, -50%);
|
|
|
|
|
|
z-index: 4;
|
|
|
|
|
|
/* Hidden until the cursor is over its column (set via `data-visible` by the
|
|
|
|
|
|
inserter's hover effect). */
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
transition: opacity var(--duration-fast, 0.12s) var(--ease-default, ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-canvas] [data-palabras-column-inserter][data-visible] {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* ── Image ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
palabras does NOT load Words' recipe, so the <figure> gets none of its layout
|
|
|
|
|
|
(and keeps the UA default `margin-inline: 40px`, and the engine's
|
|
|
|
|
|
`data-words-image-full-width` / `data-words-image-align` attrs have nothing to
|
|
|
|
|
|
react to). Mirror the essential figure rules here, scoped to the doc. */
|
|
|
|
|
|
[data-palabras-doc] figure[data-words-block='image'] {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-inline: 0;
|
|
|
|
|
|
margin-block: var(--space-4) 0;
|
|
|
|
|
|
/* The image is an ATOMIC block (the figure is contenteditable=false). Without
|
|
|
|
|
|
this, clicking it inside the contenteditable SELECTS the image and the browser
|
|
|
|
|
|
paints its native SOLID selection outline over our dashed active marker.
|
|
|
|
|
|
`user-select` inherits, so this covers the img / tile / placeholder / caption. */
|
|
|
|
|
|
user-select: none;
|
palabras: menú de inserción (tipos+iconos), columnas visibles y "+" por columna
Insertar bloque -> rejilla de tipos:
- palabras-block-types.ts (nuevo): lista curada de 10 (Párrafo/Encabezado/Imagen/
Lista/Separador/Cita/Código/Callout/Columnas/Tabla; headings y listas
colapsados; imagen incluida) con icono explícito por tipo (los definió el
usuario). Sale del registro del motor por id, sin duplicar create().
blockToInsert rellena placeholder en heading/list; imagen via pickImageFile.
- Iconos en el menú de acciones (Settings2/LayersPlus/ArrowBigUp/ArrowBigDown/
Trash2).
- Menú PORTALIZADO (DropdownMenu.Portal): el slot del grip tiene transform, que
lo hace bloque contenedor de los position:fixed -> la colisión de floating-ui
se desajustaba y el submenú se volteaba a la izquierda. Portalizar lo saca del
slot transformado.
Columnas (palabras.css, espejo de Words words.css):
- Un bloque de columnas vacío se renderizaba con ALTURA 0 (palabras no carga
words.css), y de ahí salían 4 bugs de la MISMA raíz: invisible, grip "se
vuelve loco" (las bandas Voronoi-on-Y de blockAtY se degeneran con altura 0),
no se podía seleccionar (0px sin área clicable) y el insert caía off-by-one.
Fix: flex-row + min-block-size 3em + borde dashed por columna (Words ya lo
documentaba: "without this, an empty columns block collapses to 0 height").
"+" por columna (palabras-column-inserter.svelte nuevo):
- Overlay "+" sobre cada columna VACÍA -> la misma rejilla (sin Columnas) ->
insertBlockInColumn(columnsIdx, colIdx, block) (comando del motor, como el
inspector de Words). Posicionado frame-relative, menú portalizado,
data-words-external-tool. El "+" desaparece al llenarse la columna.
Deuda anotada en continuar-palabras.md (decisión del usuario: al cerrar el
componente): labels -> langs, morfo de palabras. Pendiente abierto: grip "fuera
de la card" (el offsetParent del slot cae al marco exterior).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-doc] figure[data-words-block='image'] img {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
max-inline-size: 100%;
|
|
|
|
|
|
block-size: auto;
|
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Float intent — only emitted by the engine when NOT full width. */
|
|
|
|
|
|
[data-palabras-doc] figure[data-words-block='image'][data-words-image-align='left'] {
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-doc] figure[data-words-block='image'][data-words-image-align='right'] {
|
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Full width — span the whole content column (the default for new images). */
|
|
|
|
|
|
[data-palabras-doc] figure[data-words-block='image'][data-words-image-full-width] {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
inline-size: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-doc] figure[data-words-block='image'][data-words-image-full-width] img {
|
|
|
|
|
|
inline-size: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-doc] figure[data-words-block='image'] figcaption {
|
|
|
|
|
|
margin-block-start: var(--space-2);
|
|
|
|
|
|
font-size: var(--font-size-sm);
|
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* ── Image fit modes (Fill / Fit / Crop / Tile) ───────────────────────────────
|
|
|
|
|
|
The FRAME (fixed 16:9 + overflow clip) lives on the IMG / tile, NOT the figure,
|
|
|
|
|
|
so the figcaption stays OUTSIDE the clip and remains visible below it. */
|
|
|
|
|
|
[data-palabras-doc] figure[data-words-block='image'][data-words-image-fit] {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: stretch;
|
|
|
|
|
|
inline-size: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-doc]
|
|
|
|
|
|
figure[data-words-block='image'][data-words-image-fit]
|
|
|
|
|
|
:where(img, [data-words-image-tile]) {
|
|
|
|
|
|
inline-size: 100%;
|
|
|
|
|
|
aspect-ratio: 16 / 9;
|
|
|
|
|
|
block-size: auto;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
|
background: var(--color-surface-muted);
|
|
|
|
|
|
/* fill / crop default to cover; fit / tile override below. */
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-doc] figure[data-words-image-fit='fit'] img {
|
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Tile — the image is a repeated background on a div that fills the frame. */
|
|
|
|
|
|
[data-palabras-doc] figure[data-words-image-fit='tile'] [data-words-image-tile] {
|
|
|
|
|
|
background-repeat: repeat;
|
|
|
|
|
|
background-size: auto;
|
|
|
|
|
|
background-position: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ── Image placeholder ────────────────────────────────────────────────────
|
|
|
|
|
|
Empty-src state (`{ type: 'image', src: '' }`): the engine renders
|
|
|
|
|
|
`[data-words-image-placeholder]` (a div + two label spans). palabras shows ONLY
|
|
|
|
|
|
a large default image SVG — a clean full-width drop-zone card; the URL is filled
|
|
|
|
|
|
from the inspector, so the engine's label/hint text spans are hidden. */
|
|
|
|
|
|
[data-palabras-doc] [data-words-image-placeholder] {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
inline-size: 100%;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
min-block-size: 11rem;
|
|
|
|
|
|
padding: var(--space-6) var(--space-4);
|
|
|
|
|
|
border: 1.5px dashed var(--color-neutral-border);
|
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
|
background: var(--color-surface-muted);
|
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
|
user-select: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The icon ONLY — larger (lucide `image`, muted grey). */
|
|
|
|
|
|
[data-palabras-doc] [data-words-image-placeholder]::before {
|
|
|
|
|
|
content: '';
|
|
|
|
|
|
inline-size: 4.5rem;
|
|
|
|
|
|
block-size: 4.5rem;
|
|
|
|
|
|
background: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%239aa0aa'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Crect%20width='18'%20height='18'%20x='3'%20y='3'%20rx='2'%20ry='2'/%3E%3Ccircle%20cx='9'%20cy='9'%20r='2'/%3E%3Cpath%20d='m21%2015-3.086-3.086a2%202%200%200%200-2.828%200L6%2021'/%3E%3C/svg%3E")
|
|
|
|
|
|
center / contain no-repeat;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The engine's placeholder text is hidden — palabras keeps just the icon. */
|
|
|
|
|
|
[data-palabras-doc] [data-words-image-placeholder-label],
|
|
|
|
|
|
[data-palabras-doc] [data-words-image-placeholder-hint] {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
palabras: menú de inserción (tipos+iconos), columnas visibles y "+" por columna
Insertar bloque -> rejilla de tipos:
- palabras-block-types.ts (nuevo): lista curada de 10 (Párrafo/Encabezado/Imagen/
Lista/Separador/Cita/Código/Callout/Columnas/Tabla; headings y listas
colapsados; imagen incluida) con icono explícito por tipo (los definió el
usuario). Sale del registro del motor por id, sin duplicar create().
blockToInsert rellena placeholder en heading/list; imagen via pickImageFile.
- Iconos en el menú de acciones (Settings2/LayersPlus/ArrowBigUp/ArrowBigDown/
Trash2).
- Menú PORTALIZADO (DropdownMenu.Portal): el slot del grip tiene transform, que
lo hace bloque contenedor de los position:fixed -> la colisión de floating-ui
se desajustaba y el submenú se volteaba a la izquierda. Portalizar lo saca del
slot transformado.
Columnas (palabras.css, espejo de Words words.css):
- Un bloque de columnas vacío se renderizaba con ALTURA 0 (palabras no carga
words.css), y de ahí salían 4 bugs de la MISMA raíz: invisible, grip "se
vuelve loco" (las bandas Voronoi-on-Y de blockAtY se degeneran con altura 0),
no se podía seleccionar (0px sin área clicable) y el insert caía off-by-one.
Fix: flex-row + min-block-size 3em + borde dashed por columna (Words ya lo
documentaba: "without this, an empty columns block collapses to 0 height").
"+" por columna (palabras-column-inserter.svelte nuevo):
- Overlay "+" sobre cada columna VACÍA -> la misma rejilla (sin Columnas) ->
insertBlockInColumn(columnsIdx, colIdx, block) (comando del motor, como el
inspector de Words). Posicionado frame-relative, menú portalizado,
data-words-external-tool. El "+" desaparece al llenarse la columna.
Deuda anotada en continuar-palabras.md (decisión del usuario: al cerrar el
componente): labels -> langs, morfo de palabras. Pendiente abierto: grip "fuera
de la card" (el offsetParent del slot cae al marco exterior).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
|
|
|
|
|
|
/* ── Floating inspector panel ─────────────────────────────────────────────── */
|
|
|
|
|
|
/* The floating surface IS a framework `<Popover.Content>` — positioning (side /
|
|
|
|
|
|
align / flip / shift / fit-to-viewport), the portal, dismissal AND the surface
|
|
|
|
|
|
(border / bg / shadow / max-height) all come from the popover recipe. Here we
|
|
|
|
|
|
only RE-SHAPE the content's inner layout into a flex column with a pinned
|
|
|
|
|
|
header, a scrollable body and a pinned footer — overriding the popover recipe's
|
|
|
|
|
|
`display: grid` / padding / gap / `overflow: auto`. Doubled attribute (0,2,0)
|
|
|
|
|
|
beats `[data-popover-content]` (0,1,0) regardless of stylesheet load order. */
|
|
|
|
|
|
[data-palabras-panel-card][data-palabras-panel-card] {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
font-family: var(--font-ui);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Force the UI SANS across the whole panel + every descendant. The framework
|
|
|
|
|
|
Accordion's content archetype maps to the READING serif (Lora), which would
|
|
|
|
|
|
leak into the field labels. A DOUBLED scope attribute = (0,2,0) beats any
|
|
|
|
|
|
single-attribute component font rule regardless of code-split load order —
|
|
|
|
|
|
same technique as Words' chrome typeface guard. */
|
|
|
|
|
|
[data-palabras-panel-card][data-palabras-panel-card],
|
|
|
|
|
|
[data-palabras-panel-card][data-palabras-panel-card] * {
|
|
|
|
|
|
font-family: var(--font-ui);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Header: block-defining icon · title (grows) · trash */
|
|
|
|
|
|
[data-palabras-panel-head] {
|
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
padding: var(--space-4) var(--space-4) var(--space-3);
|
|
|
|
|
|
/* The header is the drag handle (PalabrasPanel). */
|
|
|
|
|
|
cursor: move;
|
|
|
|
|
|
user-select: none;
|
|
|
|
|
|
touch-action: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Body — header + footer stay pinned, this holds the scrollable region. It CLIPS
|
|
|
|
|
|
(overflow hidden) rather than scrolls: the panel is compact (single open
|
|
|
|
|
|
accordion) and clipping means NO scrollbar ever flaps in/out during a collapse
|
|
|
|
|
|
animation (which momentarily measures the full content height) — that flap was
|
|
|
|
|
|
the "trigger shifts right" glitch — and NO gutter is reserved (no wasted width). */
|
|
|
|
|
|
[data-palabras-panel-body] {
|
|
|
|
|
|
flex: 1 1 auto;
|
|
|
|
|
|
min-block-size: 0;
|
|
|
|
|
|
/* Scrolls within the floating-ui height cap (`--_palabras-float-max-h`) when
|
|
|
|
|
|
the panel can't fit fully on screen — header + footer stay pinned. */
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
padding: 0 var(--space-4) var(--space-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The block's defining icon, tinted by the schema's dotToken (via currentColor). */
|
|
|
|
|
|
[data-palabras-panel-icon] {
|
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-panel-icon] svg {
|
|
|
|
|
|
inline-size: 1.05rem;
|
|
|
|
|
|
block-size: 1.05rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-panel-title] {
|
|
|
|
|
|
flex: 1 1 auto;
|
|
|
|
|
|
font-size: var(--font-size-sm);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--color-content-primary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Section: collapsible title + fields */
|
|
|
|
|
|
[data-palabras-panel-section] {
|
|
|
|
|
|
margin-block-start: var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-panel-section-title] {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
inline-size: 100%;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
font: inherit;
|
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-panel-section-title] svg {
|
|
|
|
|
|
inline-size: 0.85rem;
|
|
|
|
|
|
block-size: 0.85rem;
|
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-panel-fields] {
|
|
|
|
|
|
margin-block-start: var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Pinned header section — always visible, no toggle; sits flush at the top. */
|
|
|
|
|
|
[data-palabras-panel-section][data-pinned] {
|
|
|
|
|
|
margin-block-start: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-panel-section][data-pinned] [data-palabras-panel-fields] {
|
|
|
|
|
|
margin-block-start: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* URL + upload-button row (the image `src` field). */
|
|
|
|
|
|
[data-palabras-src-group] {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-1);
|
|
|
|
|
|
inline-size: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-src-group] [data-palabras-text] {
|
|
|
|
|
|
flex: 1 1 auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Slider field — label + value ABOVE, the slider full-width BELOW (photo-app).
|
|
|
|
|
|
Compact (Apariencia stacks 7 in a row): tight label line-height + reduced gaps. */
|
|
|
|
|
|
[data-palabras-field][data-slider] {
|
|
|
|
|
|
gap: var(--space-0-5);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-field][data-slider] + [data-palabras-field][data-slider] {
|
|
|
|
|
|
margin-block-start: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-field][data-slider] [data-palabras-slider-head] {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-field][data-slider] [data-palabras-field-label],
|
|
|
|
|
|
[data-palabras-field][data-slider] [data-palabras-slider-value] {
|
|
|
|
|
|
line-height: 1.1;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-field][data-slider] [data-palabras-slider-value] {
|
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-panel-accordion] {
|
|
|
|
|
|
margin-block-start: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The OPEN (active) accordion group gets a distinct surface so it stands out. */
|
|
|
|
|
|
[data-palabras-panel-accordion] [data-accordion-item][data-state='open'] {
|
|
|
|
|
|
background: var(--color-surface-muted);
|
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Fields */
|
|
|
|
|
|
[data-palabras-field] {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: var(--space-1-5);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-field] + [data-palabras-field] {
|
|
|
|
|
|
margin-block-start: var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Inline fields (toggle · anchor · colour · number): a 2-column grid so the
|
|
|
|
|
|
label is one column and the control the other — every control's right edge
|
|
|
|
|
|
lines up across rows. */
|
|
|
|
|
|
[data-palabras-field][data-inline] {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: minmax(0, 1fr) auto;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The control column right-aligns its content (colour swatch / number field). */
|
|
|
|
|
|
[data-palabras-field][data-inline] > :not([data-palabras-field-label]) {
|
|
|
|
|
|
justify-self: end;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-field-label] {
|
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
|
color: var(--color-content-primary);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-field-todo] {
|
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
|
font-style: italic;
|
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Anchor "chip" — monospace slug, compact, right-aligned. */
|
|
|
|
|
|
[data-palabras-text] {
|
|
|
|
|
|
inline-size: 7rem;
|
|
|
|
|
|
padding: var(--space-1) var(--space-2);
|
|
|
|
|
|
border: 1px solid var(--color-neutral-border);
|
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
|
background: var(--color-surface-default);
|
|
|
|
|
|
color: var(--color-content-primary);
|
|
|
|
|
|
font: inherit;
|
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
text-align: end;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-text]:focus-visible {
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
border-color: var(--color-focus-ring);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Wide variant (URL / path) — full-width, left-aligned, normal font. */
|
|
|
|
|
|
[data-palabras-text][data-wide] {
|
|
|
|
|
|
inline-size: 100%;
|
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
|
text-align: start;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Rejected value (bad URL / bad CSS length) — red ring, value not committed. */
|
|
|
|
|
|
[data-palabras-text][data-invalid] {
|
|
|
|
|
|
border-color: var(--color-threat-border, var(--color-danger-border, crimson));
|
|
|
|
|
|
}
|
|
|
|
|
|
/* CSS-length field (Ancho / Alto) — the framework <CssField> (its own border /
|
|
|
|
|
|
bg / invalid ring / xs size) with −/+ steppers, kept compact in the inline row. */
|
|
|
|
|
|
[data-palabras-field][data-inline] [data-css-field] {
|
|
|
|
|
|
inline-size: 9rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Number field (width / height) — compact input + unit suffix, right-aligned. */
|
|
|
|
|
|
[data-palabras-number] {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-1);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-number-input] {
|
|
|
|
|
|
inline-size: 4rem;
|
|
|
|
|
|
min-inline-size: 0;
|
|
|
|
|
|
padding: var(--space-1) var(--space-2);
|
|
|
|
|
|
border: 1px solid var(--color-neutral-border);
|
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
|
background: var(--color-surface-default);
|
|
|
|
|
|
color: var(--color-content-primary);
|
|
|
|
|
|
font: inherit;
|
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
|
text-align: end;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-number-input]:focus-visible {
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
border-color: var(--color-focus-ring);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-number-unit] {
|
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* A gated field (width / height / align under "Ancho completo") dims + locks. */
|
|
|
|
|
|
[data-palabras-field][data-disabled] {
|
|
|
|
|
|
opacity: 0.45;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Footer: themed-appearance line */
|
|
|
|
|
|
/* Footer — pinned band at the card bottom (full width, muted surface, rounded
|
|
|
|
|
|
bottom corners). A flex child of the card, so it always spans edge-to-edge
|
|
|
|
|
|
regardless of the body's scrollbar. */
|
|
|
|
|
|
[data-palabras-panel-foot] {
|
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
padding: var(--space-2-5) var(--space-4);
|
|
|
|
|
|
border-block-start: 1px solid var(--color-neutral-border);
|
|
|
|
|
|
border-end-start-radius: var(--radius-lg);
|
|
|
|
|
|
border-end-end-radius: var(--radius-lg);
|
|
|
|
|
|
background: var(--color-surface-muted);
|
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-panel-edit] {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
color: var(--color-primary-text);
|
|
|
|
|
|
font: inherit;
|
|
|
|
|
|
font-size: var(--font-size-sm);
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-panel-edit][disabled] {
|
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-panel-empty] {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: var(--space-4);
|
|
|
|
|
|
font-size: var(--font-size-sm);
|
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ── Appearance popover (2nd window, opened by "Editar") ───────────────────── */
|
|
|
|
|
|
/* Also a framework `<Popover.Content>` (positioning + surface from the recipe);
|
|
|
|
|
|
we only force the UI sans across it + its descendants (the popover's content
|
|
|
|
|
|
archetype would otherwise leak the reading serif into the field labels). */
|
|
|
|
|
|
[data-palabras-appearance][data-palabras-appearance],
|
|
|
|
|
|
[data-palabras-appearance][data-palabras-appearance] * {
|
|
|
|
|
|
font-family: var(--font-ui);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* NumberField in the appearance popover — compact, fixed width so the grid's
|
|
|
|
|
|
control column stays tidy. */
|
|
|
|
|
|
[data-palabras-appearance] [data-number-field] {
|
|
|
|
|
|
inline-size: 6rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ── Help line ────────────────────────────────────────────────────────────── */
|
|
|
|
|
|
[data-palabras-help] {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: var(--font-size-sm);
|
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
[data-palabras-help] kbd {
|
|
|
|
|
|
font: inherit;
|
|
|
|
|
|
padding-inline: var(--space-1);
|
|
|
|
|
|
border: 1px solid var(--color-neutral-border);
|
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
|
background: var(--color-surface-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Mobile: the panels stay framework `<Popover>`s — the popover recipe already
|
|
|
|
|
|
keeps them within `calc(100vw - …)` and floating-ui shifts them on screen, so
|
|
|
|
|
|
no bespoke bottom-drawer layer is needed. */
|