Diseño de productos empresariales · Nuevo recurso

Sistema de diseño Duet.

Estudia las escalas de color y espaciado de LocalTapiola en Duet mediante un flujo de trabajo de intervalo de fechas, con valores de fecha ISO, validación explícita del intervalo, métodos de foco de componentes y un límite claramente diferenciado para el selector de código abierto.

Guía de OpenDesign8 capítulos39 entradas de referenciaGuía de aplicación original
01

Vista previa de la interfaz de sistema de diseño Duet

Indica las fechas de ida y vuelta, prueba un intervalo no válido y prepara un cálculo inclusivo de días.

Planificador del período de viajeAbrir ejemplo completo
Interpretación sin conexión HTML con campos de fecha nativos y valores verificados del tema predeterminado de Duet; no es el calendario en tiempo de ejecución de Duet. · Referencia de implementación
02

Referencia de diseño sistema de diseño Duet

Instantánea de la documentación pública de Duet: componentes principales del sistema, tokens predeterminados de LocalTapiola e integración documentada con 10.7.0/CDN. No es el paquete independiente @duetds/date-picker 1.x. Algunos tokens de radio siguen marcados como En revisión.

Roles predeterminados y de Turva

Fuente
Nombre / ReglaValor / ConfiguraciónUso
--color-primary#0077b3 Acción de LocalTapiola.
--color-primary-dark#004b81 Estado interactivo más oscuro.
--color-primary-darker#00619b Rol de enlace.
--color-primary-light#c6e1ee Tinte del intervalo.
--color-secondary#082a4d Texto del cuerpo.
--color-primary-turva#e61740 Acción de Turva, tema independiente.
--color-danger#d61c52 Validación.
--radius-default4pxCampos y contenedores.
--radius-pill20remBotones.
--radius-medium8px; In ReviewNo lo consideres listo para producción.

Tokens de espaciado

Fuente
Nombre / ReglaValor / ConfiguraciónUso
--space-xxx-small2pxAjustado.
--space-xx-small4pxAjustado.
--space-x-small8pxCompacto.
--space-small12pxPequeño.
--space-medium16pxPredeterminado.
--space-large20pxEntre componentes.
--space-x-large28pxSeparación mayor.
--space-xx-large36pxRelleno horizontal del botón.
--space-xxx-large48pxSección.
--size-layout-max-width1110pxAncho máximo documentado del diseño.

Escala tipográfica

Fuente
Nombre / ReglaValor / ConfiguraciónUso
$font-size-medium16px / 1remTexto del cuerpo y H5.
$font-size-large20px / 1.25remH4.
$font-size-x-large24px / 1.5remH3.
$font-size-xx-large36px / 2.25remH2.
$font-size-xxx-large48px / 3remH1.
$font-size-xxxx-large72px / 4.5remH0.

Contrato del selector de fechas

Fuente
Nombre / ReglaValor / ConfiguraciónUso
value / min / maxYYYY-MM-DD stringsEl almacenamiento usa ISO, no el texto mostrado.
duetChangedetail.value and detail.valueAsDateEvento de fecha seleccionada.
validitybadInput / valueMissing / rangeOverflow / rangeUnderflow / customErrorInspecciona antes de aceptar.
setFocus()Promise<void>Enfoca el campo, no host focus().
show() / hide()Promise<void>Gestión del calendario y del foco.
languageDeprecatedUsa html lang.
disallow-weekendsBooleanExclusión a nivel de componente.

Propiedades de acción

Fuente
Nombre / ReglaValor / ConfiguraciónUso
variation="primary"Main actionUna acción principal por vista.
submitBoolean, default falseActivación explícita del envío del formulario.
urlAnchor destinationSemántica de navegación.
expand100% containerAcción ancha.
themedefault / turva / inherited emptySelección de marca.
margin="none"Remove automatic marginLos controles de la aplicación determinan el espaciado.
03

Guía completa sistema de diseño Duet

Una guía práctica para elegir, aplicar y revisar este sistema en una interfaz real.

DESIGN-duet.md

Orientación completa, un punto de partida para la implementación y enlaces al código fuente.

Descargar la guía

Sistema de diseño Duet El sistema principal de Duet y el selector de código abierto son independientes

Mantén el contrato del paquete asociado al ejemplo

Duet ofrece componentes y fundamentos con temas para LocalTapiola y Turva. El proyecto @duetds/date-picker, publicado por separado, es una distribución de código abierto más acotada. Que compartan el nombre de una etiqueta no significa que sus propiedades, configuración de localización o variables CSS sean intercambiables con las del sistema de diseño principal actual.

De esta guía de OpenDesign

El código de integración de esta página está dirigido al sistema principal. Su vista previa sin conexión usa campos de fecha nativos y estilos originales basados en los tokens de Duet enlazados. Muestra las reglas del intervalo, pero no pretende ejecutar la ventana emergente del calendario de Duet ni su modelo de teclado.

Consulta la especificación complementaria

Sistema de diseño Duet El azul de marca y el rojo de estado cumplen funciones distintas

Usa un tema en lugar de cambiar un solo color de acento

El tema predeterminado de LocalTapiola combina una acción azul con texto del cuerpo azul oscuro. Turva tiene su propia acción roja y un rol de texto secundario. Cambiar solo el color de acción omitiría las demás relaciones del tema. La tabla actual también marca como obsoletos los tintes antiguos de Turva; copiar todas las entradas históricas de la paleta mantendría indicaciones obsoletas.

Nuestro ejemplo original para fechas de viaje usa el azul para la siguiente acción y reserva el rojo para cuando sea necesario corregir un periodo seleccionado. Es una demostración de planificación, no una recomendación de seguros, un presupuesto ni una compra.

Consulta la especificación complementaria

Sistema de diseño Duet Un texto de cuerpo de 16px sirve de base a la jerarquía

La escala no exige un título enorme en todas partes

La tabla de Duet avanza por los tamaños de 16, 20, 24, 36, 48 y 72px en los niveles mayores. Un panel de planificación integrado usa el nivel de 24px para que la acción y las etiquetas de fecha sigan destacando. El nivel de 72px, pensado para marketing, no es el predeterminado para un formulario compacto.

Los paquetes de fuentes de LocalTapiola y Turva son recursos independientes. La interpretación sin conexión usa una fuente alternativa del sistema; no incluye los archivos de fuentes de esas marcas. Una aplicación autorizada debe cargar la familia elegida mediante la ruta de fuentes documentada y usar después los tokens de peso correspondientes.

Consulta la especificación complementaria

Sistema de diseño Duet Usa los pasos reales de 20px y 28px

Duet no sigue una escala genérica de ocho píxeles

La escala incluye 12, 20, 28 y 36px, además de las potencias de dos habituales. Estos intervalos intermedios son importantes para reproducir el ritmo de los formularios del sistema. En el diseño ancho, usa una separación de 20px entre los dos campos y, al apilarlos, mantén cada etiqueta junto a su campo. El punto de corte del ejemplo es una decisión de la aplicación.

Los controles predeterminados tienen esquinas de 4px, mientras que los botones usan el radio de píldora. Algunos valores más recientes de radio para contenedores en la referencia de tokens están marcados explícitamente como En revisión; esta guía conserva ese estado en lugar de presentar como aprobados todos los tokens enumerados.

Consulta la especificación complementaria

Sistema de diseño Duet Almacena valores ISO y describe el formato visible

No analices las cadenas de visualización localizadas como datos

El valor y los límites del componente usan el formato YYYY-MM-DD. El evento de fecha seleccionada incluye una cadena y un objeto Date. Conserva la cadena para comparar y almacenar; la visualización localizada del campo es una cuestión de presentación. El idioma del documento es el punto de entrada actual para la localización, mientras que la propiedad de idioma antigua está obsoleta.

El ejemplo solo permite fechas de junio de 2027. Este intervalo fijo hace que las pruebas sean deterministas y constituye una restricción explícita del ejemplo. Un formulario de producción obtendría el intervalo permitido de la regla del producto y la explicaría junto al campo.

Consulta la especificación complementaria

Sistema de diseño Duet Una fecha válida no implica que el intervalo sea válido

Comprueba la relación cuando ambos campos sean válidos

El objeto de validez del componente describe los errores de cada campo. Una fecha de regreso anterior a la de salida aún puede ser una fecha válida del calendario, así que la aplicación debe comprobar por separado la relación entre ambas. El ejemplo nativo asigna un error claro y usa setFocus(), el método documentado que enfoca el campo de entrada interno.

El recuento es inclusivo: la salida y el regreso el mismo día equivalen a un día natural. Convertir las fechas ISO a medianoche UTC evita que un cambio de horario de verano convierta un intervalo del calendario en una duración fraccionaria. Este cálculo es lógica original de la aplicación, no una regla de precios de Duet.

  1. Rechaza los extremos ausentes, con formato incorrecto o fuera de los límites.
  2. Rechaza una fecha de regreso anterior a la de salida.
  3. Mueve el foco al campo que necesita corregirse.
  4. Prepara el intervalo inclusivo solo después de superar ambas comprobaciones.

Consulta la especificación complementaria

Sistema de diseño Duet Una acción principal no implica el envío del formulario

Duet usa una propiedad explícita para el envío

El botón del sistema principal activa el envío del formulario con submit; su valor predeterminado es false. La composición proporcionada usa una acción de clic que prepara un resultado local, así que no necesita esa propiedad. La URL de destino se define en url, lo que genera semántica de ancla.

La vista previa borra el mensaje preparado cada vez que cambia cualquiera de los campos. De lo contrario, el mensaje podría parecer confirmar valores que la persona ya ha sustituido. No se muestra ningún indicador de carga porque no hay ninguna operación asíncrona.

  • Usa una sola acción principal de preparación.
  • Indica que el resultado es local.
  • No reutilices expresiones de éxito para dar a entender que se ha creado una póliza.

Consulta la especificación complementaria

Sistema de diseño Duet Fija los recursos del sistema principal como un conjunto compatible

El ejemplo es una instantánea documentada, no una promesa de que sea la versión más reciente

La página de uso inspeccionada proporciona los componentes 10.7.0 con CSS 5.1.4; las instantáneas del índice de búsqueda ya mencionan versiones posteriores. El ejemplo mantiene deliberadamente juntas las versiones inspeccionadas directamente. Para su adopción en producción, selecciona una versión y verifica que sean compatibles CSS, las fuentes y el comportamiento de los componentes.

Los campos de fecha son elementos personalizados. Espera a que se definan antes de llamar a sus métodos, añade listeners a duetChange y lee los datos documentados en lugar de dar por sentado que se trata de un evento change nativo. No mezcles en la misma página el módulo CDN del sistema principal con el selector independiente: ambos definen la misma etiqueta.

Consulta la especificación complementaria

Sistema de diseño Duet Ejemplos de componentes del sistema de diseño

Muestra interactiva independiente completa

Guarda el documento completo como index.html y ábrelo en un navegador. Interpretación sin conexión de HTML mediante campos de fecha nativos y valores del tema predeterminado de Duet verificados; no es el entorno de ejecución del calendario de Duet.

HTML
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Duet travel period interpretation</title><style>*{box-sizing:border-box}body{margin:0;background:#f0f7fa;color:#082a4d;font:16px/1.5 system-ui,sans-serif}main{max-width:1000px;margin:auto;padding:36px 28px}.eyebrow{font-size:12px;letter-spacing:2px;text-transform:uppercase;margin-bottom:12px}h1{font-size:36px;line-height:1.2;font-weight:600;margin:0 0 20px}p{margin:0 0 20px}.hero{display:grid;grid-template-columns:1fr 300px;gap:28px;align-items:center;margin-bottom:28px}.art{height:170px;position:relative;overflow:hidden;border-radius:50%;background:#c6e1ee}.sun{position:absolute;top:25px;right:60px;width:36px;height:36px;border-radius:50%;background:white}.hill{position:absolute;background:#0077b3;width:210px;height:170px;bottom:-95px;left:-10px;transform:rotate(25deg);border-radius:24px}.hill.two{left:135px;background:#00619b;bottom:-110px;transform:rotate(-25deg)}.road{position:absolute;width:150px;height:200px;border:12px solid white;border-radius:50%;top:65px;left:95px;transform:rotate(25deg)}.card{background:white;border-radius:4px;padding:28px;box-shadow:0 2px 8px #082a4d0d;display:grid;grid-template-columns:1fr 230px;gap:28px}h2{font-size:24px;line-height:1.3;margin:0 0 20px}.fields{display:grid;grid-template-columns:1fr 1fr;gap:20px}label{display:block;font-weight:600;margin-bottom:8px}input{width:100%;max-width:100%;min-width:0;border:1px solid #909599;border-radius:4px;font:inherit;color:inherit;padding:12px;background:white}input[aria-invalid=true]{border-color:#d61c52}button{background:#0077b3;color:white;border:0;border-radius:20rem;padding:12px 36px;font:600 16px/1.5 system-ui;cursor:pointer}button:hover{background:#004b81}button:focus-visible,input:focus-visible{outline:3px solid #082a4d;outline-offset:3px}.error{font-size:14px;color:#d61c52;min-height:24px;margin:8px 0 20px}.calendar{background:#f0f7fa;border-radius:4px;padding:20px}.calendar strong{display:block;margin-bottom:12px}.grid{display:grid;grid-template-columns:repeat(7,1fr);gap:4px;font-size:12px;text-align:center}.grid span{padding:3px 0}.grid .in-range{background:#c6e1ee}.grid .endpoint{background:#0077b3;color:white;border-radius:50%}.caption{font-size:12px;margin:12px 0 0}.result{margin-top:20px;background:#e4f1f7;padding:20px;border-radius:4px}.result:empty{display:none}.boundary{font-size:12px;margin-top:28px}.hint{font-size:14px}@media(max-width:650px){main{padding:24px 16px}.hero{grid-template-columns:1fr}.art{display:none}h1{font-size:28px}.card{grid-template-columns:1fr;padding:20px}.fields{grid-template-columns:1fr;gap:16px}.calendar{max-width:300px;width:100%}button{width:100%;padding:12px 20px}}
</style><main><div class="hero"><div><div class="eyebrow">Travel period planner</div><h1>Make time for a change of scenery.</h1><p>Choose a departure and return date to explore an inclusive calendar-day count.</p></div><div class="art" aria-hidden="true"><div class="sun"></div><div class="hill"></div><div class="hill two"></div><div class="road"></div></div></div><div class="card"><section><h2>When will you travel?</h2><p class="hint" id="hint">Use dates from 1–30 June 2027. This fixed month is for demonstration.</p><form novalidate><div class="fields"><div><label for="start">Departure</label><input id="start" type="date" min="2027-06-01" max="2027-06-30" value="2027-06-10" required aria-describedby="hint error"></div><div><label for="end">Return</label><input id="end" type="date" min="2027-06-01" max="2027-06-30" value="2027-06-17" required aria-describedby="hint error"></div></div><div class="error" id="error" role="alert"></div><button type="submit">Prepare date range</button></form></section><aside class="calendar"><strong>June 2027</strong><div class="grid" id="calendar" aria-hidden="true"></div><p class="caption" id="range-caption">10–17 June selected</p></aside></div><div class="result" id="result" role="status"></div><p class="boundary">Independent HTML interpretation of Duet tokens with native date fields. The calendar image is a non-interactive range overview. No insurance quote, booking or policy is created.</p></main><script>const start=document.querySelector('#start'),end=document.querySelector('#end'),result=document.querySelector('#result'),error=document.querySelector('#error');function valid(){return start.validity.valid&&end.validity.valid&&start.value&&end.value&&end.value>=start.value}function draw(){let days=['M','T','W','T','F','S','S'].map(d=>'<span>'+d+'</span>').join('')+'<span></span>';for(let i=1;i<=30;i++){let iso='2027-06-'+String(i).padStart(2,'0'),range=valid()&&iso>=start.value&&iso<=end.value,edge=range&&(iso===start.value||iso===end.value);days+='<span class="'+(edge?'endpoint':range?'in-range':'')+'">'+i+'</span>'}document.querySelector('#calendar').innerHTML=days;document.querySelector('#range-caption').textContent=valid()?start.value+' to '+end.value:'Choose a valid range to highlight it.'}function changed(){result.textContent='';error.textContent='';start.removeAttribute('aria-invalid');end.removeAttribute('aria-invalid');draw()}[start,end].forEach(el=>el.oninput=changed);document.querySelector('form').onsubmit=e=>{e.preventDefault();let bad;if(!start.value||!start.validity.valid){error.textContent='Choose a departure date in June 2027.';bad=start}else if(!end.value||!end.validity.valid){error.textContent='Choose a return date in June 2027.';bad=end}else if(end.value<start.value){error.textContent='Return must be on or after departure.';bad=end}if(bad){bad.setAttribute('aria-invalid','true');bad.focus();result.textContent='';return}const days=(Date.parse(end.value+'T00:00:00Z')-Date.parse(start.value+'T00:00:00Z'))/86400000+1;result.textContent=days+' calendar '+(days===1?'day':'days')+', including departure and return. Local draft: '+start.value+' to '+end.value+'. No policy created.'};draw();</script></html>

Referencia de código

  • Este documento utiliza controles de fecha nativos del navegador. El ejemplo de Duet independiente usa el selector del sistema principal; no debe confundirse con el paquete independiente de código abierto para seleccionar fechas.

Validar un par de selectores de fecha del sistema principal

Dos campos etiquetados, fechas de junio con límites definidos, extremos en orden, foco en el selector no válido y recuento inclusivo de días.

HTML
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Date range draft</title>
<link rel="stylesheet" href="https://cdn.duetds.com/api/css/5.1.4/lib/duet.min.css">
<script type="module" src="https://cdn.duetds.com/api/components/10.7.0/lib/duet/duet.esm.js"></script>
<style>main{max-width:680px;margin:auto;padding:28px}.range{display:grid;grid-template-columns:1fr 1fr;gap:20px}@media(max-width:576px){.range{grid-template-columns:1fr}}</style>
<main><h1>Choose a travel period</h1><p>Demonstration only. No insurance is quoted or purchased.</p><div class="range">
<duet-date-picker id="start" label="Departure" required expand value="2027-06-10" min="2027-06-01" max="2027-06-30"></duet-date-picker>
<duet-date-picker id="end" label="Return" required expand value="2027-06-17" min="2027-06-01" max="2027-06-30"></duet-date-picker></div>
<duet-button id="prepare" variation="primary">Prepare date range</duet-button>
<p id="result" role="status"></p></main><script type="module">
await customElements.whenDefined('duet-date-picker');
const start=document.querySelector('#start'),end=document.querySelector('#end'),result=document.querySelector('#result');
function error(picker){const v=picker.validity;if(!picker.value)return 'Choose a date.';if(v&&!v.valid)return 'Choose a valid date in June 2027.';return '';}
function validate(){start.error=error(start);end.error=error(end);if(!start.error&&!end.error&&end.value<start.value)end.error='Return must be on or after departure.';return !start.error&&!end.error;}
[start,end].forEach(p=>p.addEventListener('duetChange',()=>{result.textContent='';validate()}));
document.querySelector('#prepare').addEventListener('click',async()=>{if(!validate()){await(start.error?start:end).setFocus();return;}const a=Date.parse(start.value+'T00:00:00Z'),b=Date.parse(end.value+'T00:00:00Z');result.textContent=`Local range: ${start.value} to ${end.value}, ${(b-a)/86400000+1} calendar days inclusive. No policy created.`;});
</script></html>

Referencia de código

  • Este ejemplo usa @duetds/components mediante el CDN del sistema principal, no @duetds/date-picker 1.x.
  • Los intervalos que abarcan un mismo día contienen un día natural, incluido en el recuento.
  • El cálculo en UTC evita las diferencias causadas por el horario de verano.
04

Sistema de diseño Duet Configuración del agente de IA

Punto de partida para la implementación

Composición completa del sistema principal mediante CDN, con las versiones indicadas explícitamente en la página de integración inspeccionada. Las instantáneas de búsquedas públicas también muestran versiones más recientes; fija una versión compatible en lugar de dar por sentado que esta es la más reciente. Aquí no se redistribuyen las fuentes.

HTML
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Date range draft</title>
<link rel="stylesheet" href="https://cdn.duetds.com/api/css/5.1.4/lib/duet.min.css">
<script type="module" src="https://cdn.duetds.com/api/components/10.7.0/lib/duet/duet.esm.js"></script>
<style>main{max-width:680px;margin:auto;padding:28px}.range{display:grid;grid-template-columns:1fr 1fr;gap:20px}@media(max-width:576px){.range{grid-template-columns:1fr}}</style>
<main><h1>Choose a travel period</h1><p>Demonstration only. No insurance is quoted or purchased.</p><div class="range">
<duet-date-picker id="start" label="Departure" required expand value="2027-06-10" min="2027-06-01" max="2027-06-30"></duet-date-picker>
<duet-date-picker id="end" label="Return" required expand value="2027-06-17" min="2027-06-01" max="2027-06-30"></duet-date-picker></div>
<duet-button id="prepare" variation="primary">Prepare date range</duet-button>
<p id="result" role="status"></p></main><script type="module">
await customElements.whenDefined('duet-date-picker');
const start=document.querySelector('#start'),end=document.querySelector('#end'),result=document.querySelector('#result');
function error(picker){const v=picker.validity;if(!picker.value)return 'Choose a date.';if(v&&!v.valid)return 'Choose a valid date in June 2027.';return '';}
function validate(){start.error=error(start);end.error=error(end);if(!start.error&&!end.error&&end.value<start.value)end.error='Return must be on or after departure.';return !start.error&&!end.error;}
[start,end].forEach(p=>p.addEventListener('duetChange',()=>{result.textContent='';validate()}));
document.querySelector('#prepare').addEventListener('click',async()=>{if(!validate()){await(start.error?start:end).setFocus();return;}const a=Date.parse(start.value+'T00:00:00Z'),b=Date.parse(end.value+'T00:00:00Z');result.textContent=`Local range: ${start.value} to ${end.value}, ${(b-a)/86400000+1} calendar days inclusive. No policy created.`;});
</script></html>

Una instrucción para tu proyecto

Instrucción para el agente
Crea un intervalo de fechas de Duet del sistema principal con dos selectores de fecha etiquetados, valores ISO y comprobaciones de validez. Valida por separado el orden de los extremos, llama a setFocus cuando haya errores y usa una acción local explícita para preparar el intervalo. No incluyas el paquete de selector independiente en la misma página.
05

Sistema de diseño Duet Fuentes y alcance

Referencia independiente de OpenDesign. Planificador original; no crea ninguna póliza, reserva ni cotización. Duet del sistema principal y su selector de fechas independiente de código abierto tienen contratos distintos.