Bloque de código
Líneas de código para leer, comparar y copiar, en el tipo de letra de código, que se desplazan dentro del propio bloque o saltan de línea, con una cabecera, números de línea, líneas marcadas, un prompt y un botón de copiar opcionales. Si le das el código y su lenguaje, resalta el código por sí mismo.
- Categoría
- Contención
- Tokens
comp.code-block- Nivel
- Base
- Web
tanrei-code-blockschedule - SwiftUI
TanreiCodeBlockradio_button_unchecked - Jetpack Compose
TanreiCodeBlockradio_button_unchecked - Flutter
TanreiCodeBlockradio_button_unchecked - React Native
CodeBlockradio_button_unchecked
Generado a partir de docs/status/status.json, specs/code-block.spec.json
Un título, una etiqueta de lenguaje y un botón de copiar; el código se resalta a partir de su lenguaje
// El pie de un formulario: una acción principal y una salida.
import { Button } from '@tanrei/react';
type FooterProps = { onSave: () => void; busy: boolean };
export function Footer({ onSave, busy }: FooterProps) {
const retries = 3;
return (
<footer className="footer">
<Button variant="text">Cancelar</Button>
<Button disabled={busy} onClick={onSave}>Guardar</Button>
</footer>
);
}Un bloque de código muestra código que la gente lee, compara y copia. Un nombre o un comando dentro de una frase es código en línea.
El bloque de código resalta el código por sí mismo. Dale el código y su language, y le da color a partir de tokens: no hay que instalar ningún resaltador ni escribir ningún tema ni ninguna hoja de estilos. Así, el código sigue el esquema, el nivel de contraste y el sistema de diseño como todo lo demás en la página. El resaltado es color y peso, y nada más: los caracteres, las líneas, lo que se selecciona, lo que se copia y lo que lee un lector de pantalla son los mismos con él y sin él.
En la web, el código se queda en tu documento, como un <pre><code> corriente. Está ahí sin script: para quien lee cuando el script ha fallado, para los buscadores, para la búsqueda en la página. El elemento dibuja el marco a su alrededor. Carga @tanrei/web/code.css para dar a un bloque su relleno, sus esquinas, su margen interior y su tipo de letra antes de que el elemento esté definido.
SwiftUI, Compose, Flutter y React Native están planificados: los nombres que figuran junto a cada plataforma al principio de esta página son los que les da la especificación, y todavía no se distribuye nada para ellos.
<link rel="stylesheet" href="@tanrei/web/tokens.css" />
<link rel="stylesheet" href="@tanrei/web/code.css" />
<tanrei-code-block
headline="sizes.js"
language="js"
language-label="JavaScript"
copy
copy-label="Copiar"
copied-label="Copiado"
copy-failed-label="No se pudo copiar"
>
<pre><code>const sizes = ['small', 'medium', 'large'];</code></pre>
</tanrei-code-block>
<!-- Números, líneas marcadas, un prompt. -->
<tanrei-code-block label="Ejemplo" line-numbers start-line="96" highlighted-lines="98, 100-101" highlighted-label="Resaltada">
<pre><code>…</code></pre>
</tanrei-code-block>
<tanrei-code-block label="Instalación" prompt="$" copy copy-label="Copiar" copied-label="Copiado" copy-failed-label="No se pudo copiar">
<pre><code>npm install @tanrei/web</code></pre>
</tanrei-code-block>
<script type="module">
import '@tanrei/web';
</script>import { CodeBlock } from '@tanrei/react';
import '@tanrei/web/code.css';
{/* El elemento dispone el código en líneas, así que deja que el código sea suyo: pásale el texto. */}
<CodeBlock headline="sizes.js" language="js" languageLabel="JavaScript" code={source} copy copyLabel="Copiar" copiedLabel="Copiado" copyFailedLabel="No se pudo copiar" />language es un identificador (tsx, css, sh): es aquello por lo que el bloque resalta, y no se dibuja. Las palabras de la cabecera son language-label. Un bloque construido desde un script define la propiedad code en lugar de contener un <pre><code>, y el elemento escribe él mismo el <pre><code>.
Filled y outlined, medium y small
const sizes = ['small', 'medium', 'large'];
for (const size of sizes) {
const button = document.createElement('tanrei-button');
button.setAttribute('size', size);
document.body.append(button);
}const sizes = ['small', 'medium', 'large'];
for (const size of sizes) {
const button = document.createElement('tanrei-button');
button.setAttribute('size', size);
document.body.append(button);
}Números de línea, una línea resaltada y líneas añadidas y eliminadas
const sizes = ['small', 'medium', 'large'];
for (const size of sizes) {
const button = document.createElement('button');
const button = document.createElement('tanrei-button');
button.setAttribute('size', size);
document.body.append(button);
}Una línea larga se desplaza dentro del bloque, o salta de línea
export const footer = html`<footer class="form-footer"><tanrei-button variant="text" @click=${cancel}>Cancelar</tanrei-button><tanrei-button variant="filled" @click=${save}>Guardar cambios</tanrei-button></footer>`;
// La línea de arriba es una sola línea: no cabe y nunca se corta.export const footer = html`<footer class="form-footer"><tanrei-button variant="text" @click=${cancel}>Cancelar</tanrei-button><tanrei-button variant="filled" @click=${save}>Guardar cambios</tanrei-button></footer>`;
// La línea de arriba es una sola línea: no cabe y nunca se corta.Un prompt y un límite de líneas visibles
npm install @tanrei/web
npx tanrei studio01:00 tide high, gauge 1.55 m
02:00 tide falling, gauge 1.90 m
03:00 tide low, gauge 2.25 m
04:00 tide rising, gauge 2.60 m
05:00 tide high, gauge 1.20 m
06:00 tide falling, gauge 1.55 m
07:00 tide low, gauge 1.90 m
08:00 tide rising, gauge 2.25 mDos aspectos. filled es un relleno discreto, para el código en una página. outlined no tiene relleno y va dentro de una línea fina, para el código dentro de una tarjeta o de un panel que ya tienen relleno.
El cuerpo. De forma predeterminada, una línea nunca se parte. Una línea que no cabe se desplaza dentro del bloque, y la página nunca se desplaza hacia los lados. wrap, en cambio, parte las líneas, y una fila de continuación empieza bajo el código, no bajo los números. max-lines limita la altura en líneas, y el resto se desplaza dentro del bloque.
El botón de copiar se dibuja siempre, nunca solo al pasar el cursor. Nunca tapa código que no se pueda apartar de él: un cuerpo que se desplaza gana ese mismo espacio en su final, y las filas que saltan de línea se detienen antes de su columna. Copia el código como texto sin formato, sin los números de línea, el prompt, los signos ni las líneas eliminadas. Una selección hecha a mano copia exactamente los caracteres seleccionados.
Las líneas marcadas nunca se marcan solo con el color.
| Marca | Barra en el borde izquierdo | Banda | Signo | Se lee antes de la línea |
|---|---|---|---|---|
| Resaltada | sí | sí | highlighted-label |
|
| Añadida | sí | verde tenue | + | added-label |
| Eliminada | sí | rojo tenue | − | removed-label |
Las listas de líneas son números e intervalos separados por comas ("3, 7-9"). Cuentan según los números que ve quien lee, a partir de start-line.
De derecha a izquierda. El código va siempre de izquierda a derecha: las líneas, los números y el inicio del desplazamiento están a la izquierda en todos los diseños. Solo se refleja el marco, de modo que el título pasa a la derecha y el botón de copiar a la izquierda.
Anatomía
| Parte | Descripción |
|---|---|
1 container | The block: a quiet fill (filled) or no fill inside one hairline (outlined), with small corners. As wide as its layout gives it. |
2 outline · opcional | The outlined look's edge: one hairline inside the container. |
3 header · opcional | A row above the code that holds the title, the language and, where there is one, the copy button. It has no fill and no line of its own: it is the top of the same container. |
4 title · opcional | What the code is: a file name or a few words. At the start of the header. |
5 language · opcional | The language label: the language the code is in, in the product's words. At the end of the header, before the copy button. |
6 copy-button · opcional | A Copy button (its own component) that copies the code: in the header where there is one, at the block's top end corner otherwise. |
7 body | The part that holds the lines and scrolls inside the container when they do not fit. |
8 code | The code: its lines, each on one line of the code's text style, each run in the color and weight of its syntax role where the code is highlighted. |
9 line-number · opcional | A line's number, in the gutter at the left of the code. Drawn, not text. |
10 mark · opcional | A bar at the left edge of a highlighted, an added or a removed line. |
11 sign · opcional | A plus or a minus in the gutter of an added or a removed line. Drawn, not text. |
12 prompt · opcional | A prompt drawn before each line of a block of commands. Drawn, not text. |
Propiedades
| Propiedad | Tipo | Valor predeterminado | Descripción |
|---|---|---|---|
code | string | "" | The code as plain text, lines separated by line breaks. It is what is drawn, what is highlighted and what is copied. Ignored where runs are given: their texts, joined, are the code. |
variant | filled | outlined | "filled" | filled: a quiet fill, for code on a page. outlined: no fill inside one hairline, for code inside a container that is already filled (a card, a panel). A design system may add values. |
size | small | medium | "medium" | medium sets the code in the code role (13), for a page and a panel. small sets it in the small code role (12) with less padding, for dense interfaces. A design system may add values. |
title | string | "" | A file name or a few words that say what the code is ("button.tsx", "Response"). Drawn in the header and used as the block's accessible name. |
language | string | "" | The language the code is written in, as an identifier ("tsx", "css", "swift", "sh"), read without regard to case. It is what the block highlights by (COD-19, COD-27); it is not drawn. Empty, "text" and "plain" mean code that has no syntax; a language the highlighter does not know is drawn plain. |
languageLabel | string | "" | The language in the product's words ("TypeScript", "Shell"), drawn in the header. A label only: the block does not highlight by it, and draws no label of its own for language. |
highlight | boolean | true | True: the block highlights the code by itself where it can (COD-19). False: it never does, whatever language says; runs the product gives are still drawn in their colors. |
label | string | "" | The block's accessible name where it has no title ("Example", "Request body"), in the product's language. Not drawn. A block that can scroll needs a title or a label. |
wrap | boolean | false | False: a line is never broken, and lines that do not fit scroll sideways inside the block. True: a line that does not fit continues on the next row, and nothing scrolls sideways. |
lineNumbers | boolean | false | Draws each line's number in a gutter at the left of the code. |
startLine | number | 1 | The number of the first line: an excerpt of a file keeps the file's numbers. A whole number of 1 or more. |
highlightedLines | string | "" | The lines the text around the block talks about, by number, as numbers and ranges separated by commas ("3, 7-9"). Numbers are the ones lineNumbers would draw (they count from startLine). |
addedLines | string | "" | The lines a change adds, written as highlightedLines is. |
removedLines | string | "" | The lines a change takes away, written as highlightedLines is. They are drawn and are left out of what the copy button copies. |
highlightedLabel | string | "" | What assistive technology reads before a highlighted line ("Highlighted"), in the product's language. Required while highlightedLines names a line. |
addedLabel | string | "" | What assistive technology reads before an added line ("Added"). Required while addedLines names a line. |
removedLabel | string | "" | What assistive technology reads before a removed line ("Removed"). Required while removedLines names a line. |
prompt | string | "" | A prompt ("$", ">") drawn before every line that is not empty, for a block of commands to type. It is not part of the code: it is not selected, not copied and not read. |
maxLines | number | null | The most lines shown at once; more than that scroll up and down inside the block. Null shows every line. |
copy | boolean | false | Draws a copy button that copies the code. |
copyLabel | string | "" | The copy button's accessible name ("Copy"), in the product's language. Required while copy is true. |
copiedLabel | string | "" | What the copy button announces when the code has been copied ("Copied"). Required while copy is true. |
copyFailedLabel | string | "" | What the copy button announces when the platform refuses the copy ("Could not copy"). Required while copy is true. |
Eventos
| Evento | Descripción |
|---|---|
copied | The code was copied by the block's copy button; carries the text that was copied. |
copyFailed | The platform refused the copy asked for by the block's copy button. |
Elementos
Los tipos de elemento que el componente repite. Un fixture enumera sus elementos en orden; cada plataforma nombra cada tipo según su propio estilo.
run
A piece of the code that is one kind of thing — a comment, a keyword, a string, a function's name — as the block's highlighter found it, or as the product names it where the product gives the runs. The runs' texts, in order, are the code.
| Propiedad | Tipo | Valor predeterminado | Descripción |
|---|---|---|---|
text | string | "" | The run's characters, exactly as in the code. It may hold line breaks: a run may span lines. |
syntax | plain | comment | keyword | string | constant | function | type | tag | property | punctuation | "plain" | What the run is. comment: a comment or a documentation comment. keyword: one of the language's own words (a keyword, a storage or modifier word, an at-rule, an annotation, an operator written as a word). string: text written in the code (a string, a character, a regular expression, the value of a markup attribute). constant: a value that is not text (a number, a boolean or null, a named constant, a unit, a color value, an escape inside a string). function: the name of a function or a method, declared or called. type: the name of a type (a class, a structure, an interface, an enumeration, a built-in type, a namespace). tag: what markup and style sheets address (an element's name, a component's name in markup, a selector). property: a name that belongs to something (a property, a key, a markup attribute, a style declaration's property, a command's option). punctuation: brackets, separators and the delimiters of markup. plain: none of these — variables, parameters, operators written as signs, text between tags. The values are those of the token axis comp.code-block.syntax and plain; a design system may add values to that axis. |
| Especificación | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| Elemento | span | TanreiCodeRun | TanreiCodeRun | TanreiCodeRun | CodeRun |
text · Propiedad | textContent | text | text | text | text |
syntax · Propiedad | data-syntax | syntax | syntax | syntax | syntax |
| Notas | A <span data-syntax="keyword"> inside the light-DOM <code>; text outside any such span is plain. Not a custom element. It is what the element's highlighter writes, what highlightToHtml writes where the page is made, and what a product's own highlighter writes — one form, so the stylesheet and the element cannot tell them apart and do not need to. | TanreiCodeRun(_ text: String, _ syntax: TanreiSyntaxRole = .plain), given as runs:; AttributedString ranges carrying a tanreiSyntax attribute are accepted in its place, which is what a highlighter that returns an attributed string gives. | TanreiCodeRun(text, syntax = TanreiSyntaxRole.Plain) in runs; an AnnotatedString whose ranges carry a TanreiSyntaxRole annotation is accepted in its place. | TanreiCodeRun(text, [TanreiSyntaxRole syntax = TanreiSyntaxRole.plain]) in runs:; TanreiCodeBlock.fromSpans takes the TextSpan tree of a highlighter with a function that names each span's role. | An entry of the runs prop: { text: 'const', syntax: 'keyword' }. |
Estados
Comportamiento
Requisitos en formato EARS. Cada implementación de plataforma se prueba contra estos identificadores. El texto de los requisitos se muestra tal como está en la especificación, en inglés.
| ID | Requisito |
|---|---|
COD-1 | THE code block SHALL draw a container filled with the container color of its variant (comp.code-block.filled.container.color for filled, comp.code-block.outlined.container.color for outlined), with corners comp.code-block.container.shape eased by sys.shape.corner-smoothing and no elevation, as wide as its layout gives it and never wider, and SHALL bring no space outside itself: the layout it sits in spaces it (PRS-15). WHILE variant is outlined it SHALL draw an edge comp.code-block.outline.width wide in comp.code-block.outline.color inside the container's size, following its corners; WHILE variant is filled it SHALL draw no edge. |
COD-2 | THE code block SHALL set the code in the text style of its size, taken whole (comp.code-block.code.typescale.medium for medium, and so on), in comp.code-block.code.color where a run names no syntax role, every line of the code on one line of that style; it SHALL draw the characters as given — every space kept, a tab advancing to the next multiple of comp.code-block.code.tab-size columns, nothing hyphenated, transformed, translated or spell-checked — and SHALL draw no empty last line for one line break at the end of the code. |
COD-3 | THE code block SHALL pad the code by the block padding of its size above the first line and below the last (comp.code-block.container.padding-block.medium for medium, and so on) and by the inline padding of its size (comp.code-block.container.padding-inline.medium for medium, and so on) before the start of the lines, or of the gutter where there is one (COD-13), and after the end of the longest line. |
COD-4 | WHILE wrap is false THE code block SHALL NOT break a line; WHEN a line is wider than the body THE body SHALL scroll sideways inside the container, by pointer, touch, the wheel and the keyboard (COD-7), the page SHALL NOT scroll sideways for it, and the padding after the longest line SHALL scroll with the code, so that its last character can be brought clear of the container's edge. The header and the copy button SHALL NOT scroll with the code. A gesture or a wheel movement up or down over a body that cannot scroll that way SHALL scroll the page. |
COD-5 | WHILE wrap is true THE code block SHALL continue a line that does not fit on following rows: it breaks first at the spaces of the line and, where a part with no space is wider than the row, between any two characters; a following row SHALL begin at the column where the line's code begins (after the gutter and the prompt), draw no mark of its own and add no character; the body SHALL NOT scroll sideways; and what is selected or copied SHALL hold the line breaks of the code alone. |
COD-6 | WHILE maxLines is a number and the code has more lines than that THE body SHALL be maxLines lines of the code's text style high plus its paddings and SHALL scroll up and down inside the container; the header and the copy button SHALL NOT scroll with it. WHILE maxLines is null, or the code has no more lines than it, THE code block SHALL be as tall as its lines. |
COD-7 | WHILE the body can scroll, sideways or up and down, THE body SHALL be one stop in the focus order after the copy button, SHALL scroll by the arrow keys, Page Up, Page Down, Home and End as its platform scrolls any region, and, WHEN it has keyboard focus, SHALL show the focus indicator inside the container's edge, its corners following comp.code-block.container.shape, as sys.behavior.focus-style names (ring or glow), without animation; focus gained by pointer or touch SHALL NOT show it; a platform that cannot draw a choice SHALL fall back to the first value and state that in its README. WHILE the body cannot scroll it SHALL NOT be a stop in the focus order. |
COD-8 | WHILE title or languageLabel is not empty THE code block SHALL draw a header above the code, in the same container, with no fill and no line of its own: the title at the start, in comp.code-block.title.typescale and comp.code-block.title.color, on one line, laid out as text of its own direction and cut with an ellipsis at the end of that direction when it does not fit; the language label (languageLabel) at the end, before the copy button where there is one, in comp.code-block.language.typescale and comp.code-block.language.color, never cut; its items comp.code-block.header.gap apart. The header's row SHALL begin comp.code-block.copy-button.inset from the container's top edge, be as tall as the copy button where there is one and as one line of the title's text style otherwise, with its items centred on it; the title SHALL begin at the inline padding of COD-3; and the code SHALL begin comp.code-block.header.padding-block-end below the row in place of the block padding of COD-3. |
COD-9 | WHILE copy is true THE code block SHALL place a Copy button (CPY) — icon-only, of the text variant and the small size, named by copyLabel, with copiedLabel and copyFailedLabel as its two announcements — comp.code-block.copy-button.inset from the container's top edge and from its end edge: at the end of the header where there is one, and over the top end corner of the body otherwise. THE copy button SHALL be drawn whenever the block is, never only under a pointer or on focus; SHALL come before the body in the focus order and in reading order; and SHALL keep its place and size in every form it takes (CPY-7). THE container SHALL be at least as tall as the copy button plus twice that inset. |
COD-10 | WHILE copy is true and there is no header THE code block SHALL keep code readable beside the button: WHILE wrap is false the padding after the longest line (COD-3, COD-4) SHALL be widened by the button's width and that inset, so that any line's end can be scrolled clear of the button; WHILE wrap is true every row SHALL end before the button's column. THE copy button SHALL stand on a patch of the container's own color where code can pass under it. |
COD-11 | THE copy button of COD-9 SHALL copy the code as plain text: its lines in order, joined by line breaks, without line numbers, prompts, signs or labels, without the lines removedLines names, and with no line break that wrapping added. WHEN the Copy button emits copied or copyFailed THE code block SHALL emit its own event of that name, copied carrying the text. |
COD-12 | WHEN a person selects in the body THE code block SHALL select characters of the code alone: line numbers, prompts, signs, bars and the labels of COD-15 and COD-16 SHALL NOT be selectable and SHALL NOT be part of what a copied selection holds; a copied selection SHALL hold the selected characters, removed lines included where they were selected, with the code's own line breaks. |
COD-13 | WHILE lineNumbers is true THE code block SHALL draw before each line its number, counting from startLine, in the code's text style with digits of one width, in comp.code-block.line-number.color, right-aligned in a column as wide as the widest number drawn; a line that wraps SHALL carry its number on its first row only. The gutter — from the left: the bar's column (COD-15), the numbers, the signs (COD-16) — SHALL stand comp.code-block.line-number.gap from the start of the code, SHALL stay in view on the container's color while the code scrolls sideways under it, and SHALL be kept from assistive technology. |
COD-14 | THE code block SHALL read highlightedLines, addedLines and removedLines as numbers and ranges separated by commas, each number being a line's number as COD-13 counts it whether or not numbers are drawn; it SHALL ignore a number outside the code and an entry it cannot read, and in debug builds report it. A line named as both added and removed SHALL be drawn removed, and in debug builds reported; a line named as highlighted and as added or removed SHALL be drawn as both. |
COD-15 | WHILE a line is highlighted THE code block SHALL draw behind all of its rows a band of comp.code-block.line.highlighted.container.color from the container's left edge to its right, and at the container's left edge a bar comp.code-block.line.mark.width wide in comp.code-block.line.highlighted.mark.color as tall as the line; and SHALL expose highlightedLabel to assistive technology before the line's text, without drawing it. The line's code SHALL keep its colors. |
COD-16 | WHILE a line is added or removed THE code block SHALL draw at the container's left edge a bar comp.code-block.line.mark.width wide, as tall as the line, and in the gutter after the numbers a sign in the code's text style — a plus (+) for an added line, a minus (−) for a removed one — both in comp.code-block.line.added.mark.color for an added line and comp.code-block.line.removed.mark.color for a removed one; and SHALL expose addedLabel or removedLabel before the line's text, without drawing it. THE code block SHALL also draw behind the line, from the container's left edge to its right and under the bar, the sign and the code, a band in comp.code-block.line.added.container.color for an added line and comp.code-block.line.removed.container.color for a removed one, so that a reader scanning the block tells added from removed lines whatever the colors of their code. The line's code SHALL keep its syntax colors, which are held on these bands at the contrast of code text, and SHALL NOT be struck through: the sign and the label say what the line is, never the color alone. |
COD-17 | WHILE prompt is not empty THE code block SHALL draw the prompt and one space of the code's text style before the first row of every line that is not empty, in comp.code-block.prompt.color, after the gutter; following rows of a wrapped line SHALL begin under the code, not under the prompt; the prompt SHALL be kept from assistive technology and is not code (COD-11, COD-12). |
COD-18 | THE code block SHALL draw each run in the color of its syntax role — comp.code-block.syntax.color.comment for comment, comp.code-block.syntax.color.keyword for keyword, comp.code-block.syntax.color.string for string, comp.code-block.syntax.color.constant for constant, comp.code-block.syntax.color.function for function, comp.code-block.syntax.color.type for type, comp.code-block.syntax.color.tag for tag, comp.code-block.syntax.color.property for property, comp.code-block.syntax.color.punctuation for punctuation, and comp.code-block.code.color for plain — and with the weight of the code's text style plus the emphasis of its role (comp.code-block.syntax.emphasis.keyword for keyword, and so on; none for plain), whether the block's highlighter found the run (COD-19) or the product gave it (COD-29). A syntax role SHALL change a run's color and weight alone — never its size, family, spacing, slant, decoration or background — so that highlighted code and the same code drawn plain have the same lines, columns and size. A role a design system added to the axis SHALL be drawn from that role's two tokens in the same way. |
COD-19 | WHILE highlight is true, the product gives no runs (COD-29), language names a language the highlighter knows (COD-27) and the code is no longer than comp.code-block.syntax.max-length characters THE code block SHALL highlight the code by itself: its highlighter reads the code as that language and names the syntax role of every run, and the block draws the runs as COD-18 says. Highlighting SHALL change nothing but colors and weights: the code's characters, its lines, what is selected, what is copied (COD-11, COD-12) and what assistive technology reads (COD-22) are those of the code as given. In every other case THE code block SHALL draw all of the code in comp.code-block.code.color. WHEN a run names a syntax role the active design system does not have THE code block SHALL draw that run as plain, and in debug builds report the unknown role. |
COD-20 | THE code block SHALL lay out the body left to right in every layout direction (PRS-26): lines begin at the left, the gutter, the bars and the bands' start are at the left, the prompt is at the left of its line, and sideways scrolling starts at the left. WHERE the layout direction is right-to-left THE code block SHALL mirror the frame alone: the title at the right of the header, the language and the copy button at the left, and the copy button of a block without a header at the top left corner; the paddings of COD-10 SHALL then apply at the button's side. A comment written in a right-to-left script SHALL be laid out by the bidirectional algorithm inside its left-to-right line. |
COD-21 | THE code block SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the code block is shown THE code block SHALL draw itself for the new direction at once, without a motion of its own, keeping its scroll offsets, its selection and its focus. |
COD-22 | THE code block SHALL expose its code to assistive technology as text in reading order, line after line, with the role of code where the platform has one, inside a group named by title where it is not empty and by label otherwise; the language label SHALL be read as text of the header and language itself SHALL NOT be read; a syntax role SHALL NOT be exposed, and colors arriving or changing (COD-28, COD-30) SHALL NOT be announced; and WHEN code, the runs or a marked line changes THE code block SHALL draw and expose the new content at once, without a motion, SHALL NOT announce the change, and SHALL keep the body's scroll offsets where the content still reaches them. |
COD-23 | WHERE the platform draws with a person's own forced colors THE code block SHALL draw an edge comp.code-block.outline.width wide in the platform's text color inside its container in every variant; its code in the platform's text color whatever syntax role a run names, every run keeping the weight of its role; line numbers, prompts, signs and bars in that text color; and no band: a highlighted line is then marked by its bar, and an added or a removed line by its bar and its sign. |
COD-24 | WHILE the text size is enlarged THE code block SHALL grow its lines, its gutter and its header with the text, keeping its paddings and its corners, and the copy button SHALL keep the size and the target of its own spec; at twice the text size in a container 320 wide the body SHALL scroll inside the container (COD-4) or wrap (COD-5), the title SHALL be cut as COD-8 says, and the page SHALL NOT scroll sideways. |
COD-25 | IF code is empty and no runs are given THE code block SHALL draw nothing, take no space and expose nothing, and in debug builds report that a code block has no code. WHILE copy is true and copyLabel, copiedLabel or copyFailedLabel is empty, WHILE a line is marked and the label of its mark (highlightedLabel, addedLabel, removedLabel) is empty, and WHILE the body can scroll and both title and label are empty THE code block SHALL still draw, and in debug builds report what is missing. WHEN variant or size names a value the active design system does not have THE code block SHALL render with the default value, and in debug builds report the unknown value; a startLine below 1 or not whole SHALL be taken as 1 and a maxLines below 1 as null. |
COD-26 | THE code block SHALL NOT take a press itself and has no hovered, pressed, selected or disabled state: it draws no state layer and no ripple. What is pressed inside it is its Copy button, which answers a press as its own spec says (CPY-12 to CPY-14). |
COD-27 | THE code block SHALL read language as an identifier, without regard to case and to space around it, and match it against the names and the other names of the languages its highlighter knows. THE highlighter the code block brings SHALL know these languages: markup (html, xml, svg), css, javascript (js, mjs, cjs, jsx), typescript (ts, tsx), json (jsonc), shell (sh, bash, zsh), swift, kotlin (kt) and dart; a product SHALL be able to add a language, to replace one, and to replace the highlighter whole, once for all the code blocks it draws. WHILE language is empty, "text", "plain" or "plaintext" THE code block SHALL draw the code plain and report nothing; WHILE language names a language the highlighter does not know it SHALL draw the code plain, and in debug builds report the unknown language once for each name. THE code block SHALL NOT guess a language from the code, from title or from languageLabel. |
COD-28 | THE code block SHALL draw the code at once, plain, without waiting for its highlighter: no part of the highlighter is loaded for a block that will not use it, and a block that waits for it is complete in every other respect. WHEN the runs are ready THE code block SHALL draw every color and weight in one frame, without a motion and without a fade, moving no line and no character, changing the size of nothing, and keeping the body's scroll offsets, the selection and the focus. WHERE the runs are already with the code when the block is first drawn (code highlighted where the page was made) THE code block SHALL draw them in its first frame. |
COD-29 | WHERE the product gives runs THE code block SHALL take the runs' texts, joined in order, as the code, SHALL draw them as COD-18 says, and SHALL NOT highlight the code itself, whatever language and highlight say: code that was highlighted where the page was made, or by a highlighter the product chose for one block, is drawn as it was given. Code given with the statement that it has been highlighted and has no run with a role SHALL be drawn plain in the same way. |
COD-30 | WHEN code, language or highlight changes while the block is shown THE code block SHALL draw the new code at once and highlight it again; a result for code that is no longer the code shown SHALL be dropped, never drawn. WHERE its highlighter can answer in the same frame THE code block SHALL draw the new code with its colors in that frame. Otherwise the lines before the first line that changed SHALL keep their colors until the new runs are ready, and the lines from there on SHALL be drawn plain until then, so that code that grows at its end does not lose and regain its colors with every piece. |
COD-31 | WHILE the code is longer than comp.code-block.syntax.max-length characters THE code block SHALL draw all of it plain — never a part of it highlighted — and in debug builds report that the code is too long to highlight. Runs the product gives are drawn whatever their length. |
COD-32 | IF the highlighter cannot be loaded, fails while it reads the code, or answers with runs whose texts do not join to the code exactly THEN THE code block SHALL draw the code plain, SHALL keep everything else it draws and does — the header, the copy button, the lines, the marks, scrolling — and in debug builds report what failed. A failure in one block SHALL NOT change another block. |
COD-33 | WHILE addedLines or removedLines names a line THE code block SHALL highlight the code as the two texts it holds: the lines that are not removed, in order, are read as one text — the code as it is after the change, which is what is copied (COD-11) — and color the lines that are not removed; the lines that are not added, in order, are read as another — the code as it was before — and color the removed lines. A highlighted line and an added or a removed line SHALL keep the colors of their runs over the band and beside the bar and the sign (COD-15, COD-16). |
COD-34 | WHERE the platform has ARIA attributes (the web), WHEN the product sets an aria- attribute on the host, by the attribute or by the ARIA property that reflects it, THE code block SHALL move it to the data-aria- attribute of the same name, so that the host is no second node with its name, description or state, and SHALL expose it on its body, which SHALL be a group WHILE it has a name: its title or label, or one given so — an id reference by the elements it names in the host's tree or a tree around it, a description after its own. WHILE the title or label is not empty it is the body's name, over a name given so; a name given so SHALL count as the body's name for the report of COD-25. WHEN the product removes the aria- attribute or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaDescribedByElements…) to null, THE code block SHALL take it off that element. |
Correspondencia de nombres por plataforma
Paridad no significa API idénticas. Cada plataforma expresa el mismo contrato con su propio estilo.
| Especificación | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| Componente | tanrei-code-block | TanreiCodeBlock | TanreiCodeBlock | TanreiCodeBlock | CodeBlock |
code · Propiedad | code | code | code | code | code |
variant · Propiedad | variant | variant | variant | variant | variant |
size · Propiedad | size | size | size | size | size |
title · Propiedad | headline | title | title | title | title |
language · Propiedad | language | language | language | language | language |
languageLabel · Propiedad | languageLabel | languageLabel | languageLabel | languageLabel | languageLabel |
highlight · Propiedad | highlight | highlightsSyntax | highlightSyntax | highlightSyntax | highlight |
label · Propiedad | label | label | label | label | label |
wrap · Propiedad | wrap | wrapsLines | softWrap | softWrap | wrap |
lineNumbers · Propiedad | lineNumbers | showsLineNumbers | showLineNumbers | showLineNumbers | showLineNumbers |
startLine · Propiedad | startLine | startLine | startLine | startLine | startLine |
highlightedLines · Propiedad | highlightedLines | highlightedLines | highlightedLines | highlightedLines | highlightedLines |
addedLines · Propiedad | addedLines | addedLines | addedLines | addedLines | addedLines |
removedLines · Propiedad | removedLines | removedLines | removedLines | removedLines | removedLines |
highlightedLabel · Propiedad | highlightedLabel | highlightedLabel | highlightedLabel | highlightedLabel | highlightedLabel |
addedLabel · Propiedad | addedLabel | addedLabel | addedLabel | addedLabel | addedLabel |
removedLabel · Propiedad | removedLabel | removedLabel | removedLabel | removedLabel | removedLabel |
prompt · Propiedad | prompt | prompt | prompt | prompt | prompt |
maxLines · Propiedad | maxLines | lineLimit | maxLines | maxLines | maxLines |
copy · Propiedad | copy | showsCopyButton | showCopyButton | showCopyButton | copy |
copyLabel · Propiedad | copyLabel | copyLabel | copyLabel | copyLabel | copyLabel |
copiedLabel · Propiedad | copiedLabel | copiedLabel | copiedLabel | copiedLabel | copiedLabel |
copyFailedLabel · Propiedad | copyFailedLabel | copyFailedLabel | copyFailedLabel | copyFailedLabel | copyFailedLabel |
copied · Evento | copied | onCopied | onCopied | onCopied | onCopied |
copyFailed · Evento | copyfailed | onCopyFailed | onCopyFailed | onCopyFailed | onCopyFailed |
| Notas | <tanrei-code-block headline="sizes.js" copy copy-label="Copy" …><pre><code>…</code></pre></tanrei-code-block>: the code stays in the light DOM as an ordinary <pre><code>, so it is in the document without script — for a reader whose script failed, for search engines and crawlers, for find-in-page and for the browser's own copy — and the element decorates it. The shadow root draws the header, the copy button (<tanrei-copy-button>), the focus ring and the scrolling body, and slots the <pre>; the code is read from the slotted <code>'s text, and the code attribute is for a block built by script (the element then writes the <pre><code> itself). Runs are <span data-syntax="…"> in that <code> (COD-18); the stylesheet colors them with ::slotted being unable to reach them, so the rules live in the optional light-DOM stylesheet @tanrei/web/code.css, scoped to tanrei-code-block, which also gives a not-yet-upgraded or script-less block its fill, corners, padding, face and overflow-x: auto through tanrei-code-block:not(:defined) and a bare .tanrei-prose pre (the pre rules are there, not in prose.css). Lines: the element needs one box per line for numbers, marks, the prompt and wrapping; it uses [data-line] children of the <code> where the highlighter emitted them and otherwise wraps each line in a <span data-line> on upgrade, moving nodes and splitting text nodes and spans at line breaks without adding or removing a character, so textContent is unchanged. Line numbers, signs and the prompt are ::before content of the line (counter(), with an empty alternative text so they are not read), user-select: none; the gutter is position: sticky; left: 0 on the container's color. highlighted-lines, added-lines and removed-lines are attributes; the element sets data-highlighted, data-added or data-removed on the lines and puts each label in a visually hidden, unselectable prefix referenced from the line. The body is the scroll container with tabindex="0", role="group" and aria-label only while it overflows (a ResizeObserver decides); dir="ltr" on the body, and the host mirrors the header by :dir() (COD-20). tab-size from the token. COD-11 writes through the copy button with a text built from the lines, not from innerText. **Highlighting.** highlight is a boolean that is true by default, so its attribute is written to turn it off: highlight="false" (the property takes a boolean). The highlighter is the module @tanrei/web/highlight: no dependency, no DOM, the same file in a browser and in Node. It exports highlight(code, language) (the runs, or null for a language it does not know), highlightToHtml(code, language, { lines }) (the inner HTML of the <code>: escaped text, <span data-syntax> runs and, with lines, one <span data-line> per line), registerLanguage(names, grammar) (a grammar, or a function that imports one), setHighlighter(fn) (replace it whole; null turns automatic highlighting off for the page), languages() and foldScope(scope) (the table that folds a dotted scope name into a syntax role, for a product that brings a highlighter of dotted scope names). The element does not import the module: a block that needs it (COD-19) imports it with import() the first time, and each grammar is a chunk of its own imported the first time its language is asked for, so a page whose code was highlighted where it was made, or that shows no code, loads none of it. Budget, held by a test of the built files: the core of the module at most 3 kB and each grammar at most 3 kB, compressed; tsx and jsx are the typescript and javascript grammars with markup switched on, not grammars of their own. A grammar is data — named states, each an ordered list of rules (a pattern, the role of what it matches, and the state to enter or leave) — read by one small interpreter; patterns are sticky regular expressions without nested quantifiers, so the time to read a text grows with its length and no text can stall the page. The highlighter answers in the same task once its grammar is loaded, which is what lets COD-30 draw new code with its colors in one frame; the first answer for a language waits for the import and the block is plain until then (COD-28). **Code highlighted where the page is made is first-class**: <code data-syntax-of="tsx"> holding <span data-syntax> runs, written by highlightToHtml in a build, a server or a Markdown plugin, is the product's runs (COD-29) — the element reads the attribute or any [data-syntax] it did not write itself and never highlights that code again, so the first paint has its colors and nothing is imported. The element writes its own runs as spans into the same light-DOM <code>, splitting at line boundaries, in one synchronous pass that replaces the children of each changed [data-line] and touches no other line (COD-30); it marks the runs it wrote so that it can tell them from the product's. Spans, not the CSS Custom Highlight API: a highlight registered by script cannot be in the HTML a server sends, and ::highlight() may set a color but not a weight, which is how keywords are told (COD-18); for assistive technology the two are the same, since neither adds a role. A framework that owns the children of the <code> (it renders them from state) should pass code as a property and let the element write the <pre><code>, or give the runs itself. comp.code-block.syntax.max-length is read from the generated tokens module, not from CSS. The colors are rules of @tanrei/web/code.css, one per value of the syntax axis, generated from the tokens so that a role a layer adds gets its rule. | TanreiCodeBlock(code:, runs:, variant:, size:, title:, …). The body is a ScrollView (horizontal, and vertical under lineLimit) holding a Text per line with textSelection(.enabled); the gutter is a separate, non-selectable column pinned beside it. The body's layout direction is forced to left-to-right in the environment (COD-20). The marks' labels are the line's accessibility label prefix. Automatic highlighting is not built on this platform yet; as specified, it is the same grammars, which are data, read by an interpreter written in Swift over NSRegularExpression, behind TanreiCodeHighlighter — a protocol the product may implement to bring its own — set once in the environment; highlightsSyntax: Bool = true. Until then this platform draws runs the product gives and code without runs plain, and says so in its README. | TanreiCodeBlock(code, runs, variant, size, title, …). The body is a SelectionContainer over a BasicText in horizontalScroll (and verticalScroll under maxLines), inside CompositionLocalProvider(LocalLayoutDirection provides Ltr) (COD-20); the gutter is drawn beside it and cleared from semantics. The body is focusable only while its scroll state reports a range. Automatic highlighting is not built on this platform yet; as specified, it is the same grammars read by an interpreter written in Kotlin over kotlin.text.Regex, behind a TanreiCodeHighlighter interface provided by a CompositionLocal; the runs are computed off the main thread in a produceState keyed by the code and the language, which is this platform's form of COD-30's cancellation. Until then this platform draws runs the product gives and code without runs plain, and says so in its README. | TanreiCodeBlock(code: …, runs: …, variant:, size:, title:, …). The body is a SelectableText.rich in a horizontal SingleChildScrollView with a Scrollbar, under Directionality(textDirection: TextDirection.ltr) (COD-20); the gutter is a column beside it wrapped in ExcludeSemantics. A Focus node on the body exists only while the scroll position has an extent. Automatic highlighting is not built on this platform yet; as specified, it is the same grammars read by an interpreter written in Dart over RegExp, behind a TanreiCodeHighlighter the theme carries; long code is read in an isolate. Until then this platform draws runs the product gives and code without runs plain, and says so in its README. | <CodeBlock code="…" runs={…} title="sizes.js" />. The body is a horizontal ScrollView holding <Text selectable> lines with writingDirection: 'ltr'; the gutter is a sibling column with importantForAccessibility="no-hide-descendants" and accessibilityElementsHidden. Copying goes through the Copy button (see its note on the clipboard module). Follows the app's direction where an ancestor's is not known. Automatic highlighting is not built on this platform yet; as specified, it is the highlighter module of the web package, which has no DOM, imported as it is; a highlighter prop of the provider replaces it. Until then this platform draws runs the product gives and code without runs plain, and says so in its README. |
Tokens
| Token | Alias de | Valor resuelto (claro) |
|---|---|---|
--tanrei-comp-code-block-code-color | sys.color.on-surface | |
--tanrei-comp-code-block-code-tab-size | — | 2 |
--tanrei-comp-code-block-container-padding-block-medium | sys.space.space150 | 12px |
--tanrei-comp-code-block-container-padding-block-small | sys.space.space100 | 8px |
--tanrei-comp-code-block-container-padding-inline-medium | sys.space.space200 | 16px |
--tanrei-comp-code-block-container-padding-inline-small | sys.space.space150 | 12px |
--tanrei-comp-code-block-container-shape | sys.shape.corner.small | 6px |
--tanrei-comp-code-block-copy-button-inset | sys.space.space75 | 6px |
--tanrei-comp-code-block-filled-container-color | sys.color.surface-container | |
--tanrei-comp-code-block-header-gap | sys.space.space100 | 8px |
--tanrei-comp-code-block-header-padding-block-end | sys.space.space50 | 4px |
--tanrei-comp-code-block-language-color | sys.color.on-surface-variant | |
--tanrei-comp-code-block-line-number-color | sys.color.on-surface-variant | |
--tanrei-comp-code-block-line-number-gap | sys.space.space200 | 16px |
--tanrei-comp-code-block-line-added-container-color | sys.color.success-container | |
--tanrei-comp-code-block-line-added-mark-color | sys.color.success | |
--tanrei-comp-code-block-line-highlighted-container-color | sys.color.surface-container-highest | |
--tanrei-comp-code-block-line-highlighted-mark-color | sys.color.primary | |
--tanrei-comp-code-block-line-mark-width | sys.border.thick | 2px |
--tanrei-comp-code-block-line-removed-container-color | sys.color.error-container | |
--tanrei-comp-code-block-line-removed-mark-color | sys.color.error | |
--tanrei-comp-code-block-outline-color | sys.color.outline-variant | |
--tanrei-comp-code-block-outline-width | sys.border.hairline | 1px |
--tanrei-comp-code-block-outlined-container-color | — | |
--tanrei-comp-code-block-prompt-color | sys.color.on-surface-variant | |
--tanrei-comp-code-block-syntax-color-comment | sys.color.on-surface-variant | |
--tanrei-comp-code-block-syntax-color-constant | sys.color.on-surface | |
--tanrei-comp-code-block-syntax-color-function | sys.color.on-surface | |
--tanrei-comp-code-block-syntax-color-keyword | sys.color.on-surface | |
--tanrei-comp-code-block-syntax-color-property | sys.color.on-surface | |
--tanrei-comp-code-block-syntax-color-punctuation | sys.color.on-surface-variant | |
--tanrei-comp-code-block-syntax-color-string | sys.color.on-surface | |
--tanrei-comp-code-block-syntax-color-tag | sys.color.on-surface | |
--tanrei-comp-code-block-syntax-color-type | sys.color.on-surface | |
--tanrei-comp-code-block-syntax-emphasis-comment | — | 0 |
--tanrei-comp-code-block-syntax-emphasis-constant | — | 0 |
--tanrei-comp-code-block-syntax-emphasis-function | — | 0 |
--tanrei-comp-code-block-syntax-emphasis-keyword | sys.typescale.emphasis.body | 100 |
--tanrei-comp-code-block-syntax-emphasis-property | — | 0 |
--tanrei-comp-code-block-syntax-emphasis-punctuation | — | 0 |
--tanrei-comp-code-block-syntax-emphasis-string | — | 0 |
--tanrei-comp-code-block-syntax-emphasis-tag | — | 0 |
--tanrei-comp-code-block-syntax-emphasis-type | — | 0 |
--tanrei-comp-code-block-syntax-max-length | — | 50000 |
--tanrei-comp-code-block-title-color | sys.color.on-surface | |
--tanrei-comp-code-block-code-typescale-medium | sys.typescale.code | 400 13px/1.5385 |
--tanrei-comp-code-block-code-typescale-small | sys.typescale.code-small | 400 12px/1.3333 |
--tanrei-comp-code-block-language-typescale | sys.typescale.label-small | 500 12px/1.3333 |
--tanrei-comp-code-block-title-typescale | sys.typescale.label-small | 500 12px/1.3333 |
Fixtures
Representaciones canónicas. Todas las plataformas capturan cada fixture con el mismo identificador para poder comparar los resultados.
variant: filled (43)
plain
code: <tanrei-button variant="filled">Save</ta… label: Example small
code: const sizes = ['small', 'medium', 'large… size: small label: Example titled
code: const sizes = ['small', 'medium', 'large… title: sizes.js titled-language-copy
code: const sizes = ['small', 'medium', 'large… title: sizes.js languageLabel: JavaScript copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy language-only
code: <tanrei-button variant="filled">Save</ta… languageLabel: HTML label: Example copy
code: const sizes = ['small', 'medium', 'large… label: Example copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy one-line-copy
code: npm install @tanrei/web label: Install copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy syntax
title: Footer.tsx languageLabel: TSX copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy items: 87 syntax-small
size: small label: A layer file items: 38 line-numbers
code: const sizes = ['small', 'medium', 'large… label: Example lineNumbers: true line-numbers-start
code: const sizes = ['small', 'medium', 'large… title: sizes.js lineNumbers: true startLine: 96 highlighted-lines
code: const sizes = ['small', 'medium', 'large… label: Example lineNumbers: true highlightedLines: 1, 4-5 highlightedLabel: Highlighted diff
code: const sizes = ['small', 'medium', 'large… title: sizes.js lineNumbers: true addedLines: 5-6 removedLines: 4 addedLabel: Added removedLabel: Removed copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy diff-without-numbers
code: <tanrei-button variant="filled">Save</ta… label: Example addedLines: 2 removedLines: 1 addedLabel: Added removedLabel: Removed syntax-marked
title: Footer.tsx lineNumbers: true highlightedLines: 5 addedLines: 9 highlightedLabel: Highlighted addedLabel: Added copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy items: 87 long-lines
code: export const footer = html`<footer class… label: Example long-lines-copy
code: export const footer = html`<footer class… label: Example copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy long-lines-wrapped
code: export const footer = html`<footer class… label: Example wrap: true long-lines-wrapped-numbered-copy
code: export const footer = html`<footer class… label: Example wrap: true lineNumbers: true highlightedLines: 1 highlightedLabel: Highlighted copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy max-lines
code: 01:00 tide high, gauge 1.55 m
02:00 tide… title: tide.log lineNumbers: true maxLines: 5 copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy terminal
code: npm install @tanrei/web
npx tanrei studi… languageLabel: Shell prompt: $ copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy terminal-one-line
code: flutter pub add tanrei label: Install prompt: $ copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy tabs
code: build:
node scripts/build.js
test: bui… title: Makefile japanese-comment
label: 例 items: 8 rtl
code: const sizes = ['small', 'medium', 'large… title: مثال languageLabel: JavaScript lineNumbers: true highlightedLines: 3 highlightedLabel: مميز copy: true copyLabel: نسخ copiedLabel: تم النسخ copyFailedLabel: تعذر النسخ direction: rtl rtl-copy-without-header
code: export const footer = html`<footer class… label: مثال copy: true copyLabel: نسخ copiedLabel: تم النسخ copyFailedLabel: تعذر النسخ direction: rtl rtl-ltr-title
code: <tanrei-button variant="filled">Save</ta… title: packages/web/src/components/button/butto… copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy direction: rtl auto-tsx
code: // A form's footer: one main action, one… title: Footer.tsx language: tsx languageLabel: TSX copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy auto-html
code: <!-- One main action, one way out -->
<f… language: html label: Example auto-css
code: /* The footer of a form */
@media (min-w… language: css label: Example auto-javascript
code: const sizes = ['small', 'medium', 'large… language: js label: Example auto-json
code: {
"name": "acme",
"extends": "tanrei… language: json label: Example auto-shell
code: # Install and build
pnpm add @tanrei/web… language: sh label: Example prompt: $ auto-swift
code: import SwiftUI
/// A form's footer.
str… language: swift label: Example auto-kotlin
code: @Composable
fun Footer(busy: Boolean, on… language: kotlin label: Example auto-dart
code: class Footer extends StatelessWidget {… language: dart label: Example auto-small
code: {
"name": "acme",
"extends": "tanrei… size: small language: json label: Example auto-highlighted-lines
code: const sizes = ['small', 'medium', 'large… language: js label: Example lineNumbers: true highlightedLines: 4-5 highlightedLabel: Highlighted auto-marked
code: const sizes = ['small', 'medium', 'large… language: js label: Example lineNumbers: true addedLines: 3 removedLines: 2 addedLabel: Added removedLabel: Removed copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy auto-japanese-comment
code: // 保存ボタンは 1 画面に 1 つだけ置く。
const label = '… language: js label: 例 auto-off
code: // A form's footer: one main action, one… language: tsx highlight: false label: Example auto-text
code: Tanrei 0.1.0
5 platforms, 36 component… language: text label: Example auto-unknown-language
code: IDENTIFICATION DIVISION.
PROGRAM-ID. HEL… language: cobol label: Example variant: outlined (5)
outlined
code: <tanrei-button variant="filled">Save</ta… variant: outlined label: Example small-outlined-titled
code: const sizes = ['small', 'medium', 'large… variant: outlined size: small title: sizes.js languageLabel: JavaScript copy: true copyLabel: Copy copiedLabel: Copied copyFailedLabel: Could not copy syntax-outlined
variant: outlined label: A layer file items: 38 highlighted-outlined
code: const sizes = ['small', 'medium', 'large… variant: outlined label: Example highlightedLines: 3 highlightedLabel: Highlighted auto-outlined
code: // A form's footer: one main action, one… variant: outlined language: tsx label: Example Generado a partir de specs/code-block.spec.json, tokens/comp/code-block.tokens.json, design/tokens/
El color procede de una de tres fuentes, en este orden.
- Los tramos que das tú. Código resaltado allí donde se generó la página, o por un resaltador que elegiste tú. Se dibujan tal como se dan, y el bloque nunca vuelve a resaltar ese código, digan lo que digan
languageyhighlight. - El resaltador propio del bloque, cuando
highlightes true,languagenombra un lenguaje que el bloque conoce y el código no es más largo quecomp.code-block.syntax.max-length(50 000 caracteres). - Sin resaltar, en todos los demás casos.
El código se dibuja enseguida, sin resaltar. Sus colores llegan un momento después, en un solo fotograma, y no mueven nada. El código que cambia se vuelve a leer, y el código que crece por el final conserva los colores que ya tiene.
language |
Qué ocurre |
|---|---|
html, xml, svg, css, javascript (js, mjs, cjs, jsx), typescript (ts, tsx), json (jsonc), shell (sh, bash, zsh), swift, kotlin (kt), dart |
Se resalta. No se distingue entre mayúsculas y minúsculas. |
vacío, text, plain, plaintext |
Sin resaltar. No se notifica nada. |
| cualquier otro valor | Sin resaltar. Una compilación de depuración notifica una vez el lenguaje desconocido. |
El bloque nunca adivina el lenguaje: ni por el código, ni por el headline, ni por el language-label. Un código corto es ambiguo, y una suposición equivocada colorea mal el código.
Cómo desactivarlo. highlight="false" desactiva el resaltador propio del bloque para un solo bloque. Los tramos que das tú se siguen dibujando.
Un límite. El código más largo que comp.code-block.syntax.max-length se dibuja sin resaltar, entero, nunca resaltado a medias. Un sistema de diseño puede cambiar el valor.
Con líneas añadidas y eliminadas, el bloque lee el código como los dos textos que contiene: el código posterior al cambio da color a las líneas que no están eliminadas, y el código anterior al cambio, a las eliminadas. Una línea eliminada que abre una cadena no puede colorear mal lo que sigue.
Los fallos son silenciosos. Un resaltador que no se puede cargar, que lanza una excepción o cuyos tramos, unidos, no coinciden exactamente con el código deja el código sin resaltar. La cabecera, el botón de copiar, las líneas y las marcas siguen funcionando.
Las gramáticas son pequeñas a propósito. Están pensadas para mostrar código, no para editarlo. Donde un lenguaje es difícil pueden poner un tramo en el color equivocado, pero nunca pierden un carácter.
Un sitio que genera sus páginas puede resaltar durante la compilación. Los colores están entonces en el primer pintado, y el navegador no carga ningún resaltador. highlightToHtml es la misma función que usa el elemento, y se ejecuta en Node.
import { highlightToHtml } from '@tanrei/web/highlight';
const html = await highlightToHtml(source, 'tsx');
// <tanrei-code-block language="tsx"><pre><code data-syntax-of="tsx">${html}</code></pre></tanrei-code-block>data-syntax-of en el <code> dice que el código ya está resaltado. El elemento lo dibuja tal como se da y no importa nada.
@tanrei/web/highlight es un módulo independiente, sin dependencias. El elemento lo importa la primera vez que un bloque lo necesita, y la gramática de cada lenguaje, la primera vez que se pide ese lenguaje.
| Exportación | Qué hace |
|---|---|
highlight(code, language) |
Los tramos, o null para un lenguaje que no conoce. Una promesa la primera vez que se usa un lenguaje, y un valor después. |
highlightToHtml(code, language, { lines }) |
El HTML interno del <code>. Con lines, un <span data-line> por línea. |
registerLanguage(names, grammar) |
Añade un lenguaje o sustituye uno. Una gramática son datos: estados con nombre, cada uno una lista de reglas con un patrón, un rol y el estado en el que se entra o del que se sale. Puede ser una función que importa la gramática. |
loadLanguage(language) |
Carga una gramática antes de que llegue el primer código en ese lenguaje. |
setHighlighter(fn) |
Sustituye el resaltador entero. null desactiva el resaltado automático en la página. |
languages() |
Todos los nombres por los que se conoce un lenguaje. |
foldScope(scopes, text) |
El rol que corresponde a un nombre de ámbito separado por puntos, según la tabla de más abajo. |
tokenize(grammar, code), checkGrammar(grammar) |
Para probar una gramática que escribas tú. |
Un producto que ya tiene un resaltador de su confianza también puede definir TanreiCodeBlock.highlighter como una función (code, language, signal) => runs. El bloque pregunta entonces a esa función, y el módulo de Tanrei no se carga nunca.
Rol (data-syntax) |
Qué es | En el sistema predeterminado |
|---|---|---|
comment |
Un comentario, un comentario de documentación | Más discreto que el código |
keyword |
Una de las palabras propias del lenguaje: una palabra clave, un modificador, una regla-at, una anotación, un operador escrito como palabra | Morado, un paso de peso más grueso |
string |
Texto escrito en el código: una cadena, un carácter, una expresión regular, el valor de un atributo de marcado | Verde |
constant |
Un valor que no es texto: un número, un booleano, null, una constante con nombre, una unidad, un valor de color, una secuencia de escape | Naranja |
function |
El nombre de una función o de un método, declarado o llamado | Azul |
type |
El nombre de un tipo: una clase, una estructura, una interfaz, un tipo integrado, un espacio de nombres | Verde azulado |
tag |
Aquello a lo que se dirigen el marcado y las hojas de estilos: el nombre de un elemento, el nombre de un componente en el marcado, un selector | Rojo |
property |
Un nombre que pertenece a algo: una propiedad, una clave, un atributo de marcado, una propiedad de estilo, la opción de un comando | Azul, de un tono más cercano al color del código |
punctuation |
Corchetes, llaves y paréntesis, separadores, los delimitadores del marcado | Más discreto que el código |
| sin rol (sin span) | Todo lo demás: variables, parámetros, operadores escritos como signos, texto entre etiquetas | El color del código |
Cada rol tiene dos tokens: un color (comp.code-block.syntax.color.*) y un paso de peso (comp.code-block.syntax.emphasis.*). Los roles son un eje de tokens, así que un sistema de diseño puede añadir uno. El verde y el rojo del sistema predeterminado proceden de paletas propias de la sintaxis, de un matiz cercano al de los colores de éxito y de error. Una línea añadida o eliminada se distingue por su barra, su signo y su etiqueta, nunca solo por el color. Un rol solo cambia el color y el peso, de modo que el código resaltado y el código sin resaltar tienen las mismas líneas y columnas.
Un tramo es un <span data-syntax="…"> dentro del <code>. El texto que queda fuera de un span es texto sin rol.
Los tramos que das tú se dibujan tal como se dan
{
"name": "acme",
"dials": { "size.unit": 4 },
"compact": false
}Algunos resaltadores nombran lo que encuentran con nombres de ámbito (scopes) separados por puntos, tomados de un vocabulario común. foldScope los reduce a los roles, para que tu resaltador y el de Tanrei den los mismos colores. Los ámbitos de un token se prueban del más específico hacia fuera. Dentro de un mismo ámbito gana la primera fila que coincide. Un ámbito que no coincide con ninguna fila se pasa por alto, y un token que no coincide con nada queda sin rol.
| # | El ámbito empieza por | Rol |
|---|---|---|
| 1 | comment, punctuation.definition.comment |
comment |
| 2 | constant.character.escape, constant.numeric, constant.language, constant.other, keyword.other.unit, support.constant, variable.other.constant, variable.other.enummember |
constant |
| 3 | string, punctuation.definition.string, markup.inline.raw, markup.raw |
string |
| 4 | entity.name.tag, support.class.component, meta.selector y, en las hojas de estilos, entity.other.attribute-name |
tag |
| 5 | entity.other.attribute-name, support.type.property-name, meta.property-name, meta.object-literal.key, variable.other.property, variable.other.object.property, support.type.vendored.property-name, variable.parameter.option |
property |
| 6 | entity.name.function, support.function, variable.function, meta.function-call.generic |
function |
| 7 | entity.name.type, entity.name.class, entity.name.namespace, entity.other.inherited-class, support.type, support.class, storage.type.primitive, storage.type.built-in, keyword.type |
type |
| 8 | keyword.operator cuyo texto está formado por letras (new, typeof, in) |
keyword |
| 9 | keyword.operator en los demás casos |
sin rol |
| 10 | keyword, storage, variable.language, meta.decorator, punctuation.decorator, entity.name.function.decorator, markup.heading |
keyword |
| 11 | punctuation, meta.brace, meta.delimiter |
punctuation |
| 12 | variable, meta, source, text, markup, invalid |
sin rol |
- Las comillas de una cadena forman parte de la cadena. Una secuencia de escape dentro de una cadena es una constante.
- Las claves de JSON son propiedades, y sus valores, cadenas y constantes.
- El nombre de un decorador es una palabra clave.
invalidqueda sin rol. Un componente que muestra código no lo juzga.- Añadida y eliminada son propiedades de una línea (
added-lines,removed-lines), no de un tramo.
- El elemento da una caja a cada línea envolviéndola en
<span data-line>. Mueve tus nodos y divide el texto y los tramos en los saltos de línea, y no añade ningún carácter al código. Un framework que es dueño de los hijos del<code>debería pasarcodeen su lugar, o renderizar[data-line]él mismo. - La etiqueta de una línea marcada es un
<span>oculto situado antes del código de la línea. Forma parte deltextContentdel<code>, y queda fuera de lo que se copia. - El aspecto outlined se dibuja sobre aquello en lo que se coloca. Donde eso no sea el color de superficie, indícalo en
--tanrei-code-block-ground, para que los números de línea y el botón de copiar puedan apoyarse en él mientras el código se desplaza por debajo. - Antes de que el elemento esté definido,
code.cssdibuja el relleno, las esquinas, el margen interior y los colores de los tramos. La cabecera, el botón de copiar, los números y las marcas necesitan el elemento. highlightes true de forma predeterminada, así que su atributo se escribe para desactivarlo:highlight="false". La propiedad acepta un booleano.- El elemento escribe sus propios tramos como spans dentro de tu
<code>, línea a línea, y los marca como suyos. Un framework que es dueño de los hijos del<code>debería pasarcodey dejar que el elemento los escriba. - El límite se lee de la propiedad personalizada
--tanrei-comp-code-block-syntax-max-length, tal como la cascada se la da al bloque. - Una página que usa el paquete de un solo archivo lleva el resaltador dentro. Una página que importa
@tanrei/webcomo módulos lo carga bajo demanda.
- Da un
headlineo unlabela un bloque que pueda desplazarse. El cuerpo que se desplaza es una parada de foco y necesita un nombre. - Usa
outlineddentro de una tarjeta o de un panel, yfilleden una página y sobre un contenedor de color. - Usa un prompt para un bloque de comandos que hay que escribir. El prompt no se copia.
- Da a cada bloque su
language. Usalanguage="text"para un texto que no tiene sintaxis. - Pon varios archivos o lenguajes en pestañas, cada una con un bloque de código.
- No uses un bloque de código para un nombre dentro de una frase. Usa código en línea.
- No dependas de los colores de la sintaxis para decir algo. El mismo código dibujado sin resaltar debe decir lo mismo.
- No mezcles comandos y su salida bajo un mismo prompt. Todas las líneas que no están vacías reciben el prompt. Usa dos bloques.
- Rol
group- Etiquetado
- The block is a group named by title, or by label where it has no title; inside it the code is text with the role of code where the platform has one. The copy button is named by copyLabel. Line numbers, prompts, signs and bars are hidden; a marked line is preceded by its label in words (highlightedLabel, addedLabel, removedLabel).
- Objetivo mínimo
- El área de pulsación cumple el tamaño mínimo de objetivo aunque el tamaño visual sea menor. minimum 32px · pointer 44px · spacing 8px · touch 48px
Teclado
| Teclas | Acción |
|---|---|
| Tab | Moves focus to the copy button where there is one, then to the body while it can scroll. A block that neither scrolls nor copies is not a stop. |
| Arrow keys, Page Up, Page Down, Home, End | Scroll the focused body, as the platform scrolls any region. |
| Enter / Space | On the copy button: copies the code (CPY-2). |
Notas
- The only target is the copy button, whose hit area meets the minimum target by its own spec (CPY-1, BTN-5) though it is drawn 32 high. The body is scrolled, not pressed.
- A body that scrolls must be reachable by keyboard (WCAG 2.1.1) and so is a focus stop with a visible focus indicator (COD-7); one that does not scroll is not, so a page of short examples adds no stops. A group, not a landmark: a page may hold hundreds of blocks.
- Reflow (WCAG 1.4.10): code is two-dimensional content, so sideways scrolling inside the block is allowed, and the page itself never scrolls sideways (COD-4, COD-24). wrap is for a product that prefers no sideways scrolling at all.
- A marked line is never marked by color alone (WCAG 1.4.1): a highlighted line has a bar and a band and its label; an added or a removed line has a bar, a plus or a minus and its label (COD-15, COD-16). Syntax colors carry no meaning a reader needs: the same code drawn plain says the same thing, a role is never exposed (COD-22), and keywords are also told by weight. With a red-green color vision deficiency some of the default system's six hues draw together — the green of a string and the red of a tag's name first — beside the neutral tones and the keyword's weight; nothing is lost but a distinction between kinds of words.
- Contrast: the code, the line numbers, the prompt and the signs are roles the token invariants hold at 4.5:1 on every surface (7:1 in the high-contrast schemes). Every syntax color is held by the build at the same ratios against the block's fill, against the band of a highlighted line and against every surface role, which is what an outlined block may stand on, in each system. The bars are primary, success and error, held at the same ratios, above the 3:1 a mark needs. The outlined look's edge is decorative.
- An outlined block is read on whatever it is placed on. On a surface that is correct. On a colored container (a callout) the syntax colors are not paired with that container: use filled there, which brings its own surface.
- The copy button is always drawn (COD-9): one that appears only under a pointer cannot be found by touch, and one that appears on focus moves under a keyboard user. Its confirmation is announced by the Copy button's own live region (CPY-5).
- Highlighting adds nothing for assistive technology and takes nothing away: the code is the same text before and after the colors arrive, no live region speaks of them, and the body's scroll position, the selection and the focus are kept (COD-28). Colors arrive in one frame, without a motion, so nothing flashes more than once.
- Screen readers read code as text, symbol by symbol as the person's settings say. The code block adds no line numbers or roles to that reading; the marks' labels are the only words it adds.
- Code is not translated and not spell-checked (COD-2); comments in it are the author's language and are read with the page's voice unless the product marks their language.
Generado a partir de specs/code-block.spec.json, tokens/sys/measure.tokens.json
- El código es el elemento
codedentro de un grupo que recibe su nombre deheadline, o delabel. - Un cuerpo que puede desplazarse es una parada en el orden del foco, después del botón de copiar, y se desplaza con las teclas de flecha. Un cuerpo que cabe no es una parada, así que una página de ejemplos cortos no añade ninguna.
- Los números de línea, los signos, las barras y el prompt quedan ocultos para la tecnología de apoyo. A una línea marcada la precede su etiqueta, con palabras.
- La página nunca se desplaza hacia los lados por un bloque de código, con 320 de ancho y con el texto al doble de tamaño.
- Con colores forzados, todos los aspectos ganan un borde, todo el código tiene el color del texto, y las barras y los signos se mantienen.
- El resaltado no añade nada para la tecnología de apoyo. No se expone ningún rol, y los colores que llegan no se anuncian. La selección, el foco y la posición de desplazamiento se conservan.
- Con una deficiencia de la visión del color rojo-verde, algunos de los seis matices se acercan entre sí, primero el verde de una cadena y el rojo del nombre de una etiqueta. Las palabras clave se distinguen además por el peso.
- Proporciona
copy-label,copied-labelycopy-failed-labeljunto concopy, y la etiqueta de cada marca que uses, en el idioma de tu producto. - Di en el texto que rodea al bloque qué muestra una línea resaltada. La etiqueta solo dice que está resaltada.