# Sistema de diseño ServiceTitan Anvil

## Vista previa de la interfaz del sistema de diseño ServiceTitan Anvil

Cambia entre la apariencia clara y oscura, selecciona al personal disponible y revisa un plan local sincronizado.

## Referencia de diseño del sistema de diseño ServiceTitan Anvil

Exportaciones del tema principal de @servicetitan/hammer-token 3.5.0, junto con la documentación de tematización de Anvil2.

### Pares semánticos para los modos claro y oscuro

| Nombre / Regla | Valor / Configuración | Uso |
| --- | --- | --- |
| BackgroundColorDefault | light #ffffff / dark #1a1a1a | Superficie principal de la aplicación. |
| BackgroundColorDefaultHover | light #f9f9f9 / dark #292929 | Superficie al pasar el cursor. |
| BackgroundColorPrimary | light #0265dc / dark #70b1ff | Relleno de acción principal. |
| BackgroundColorPrimaryHover | light #1d4ca3 / dark #9ecaff | Función nativa de estado al pasar el cursor sobre el botón principal. |
| BackgroundColorPrimaryFaint | light #fafcff / dark #000b19 | Fondo seleccionado discreto. |
| ForegroundColorSubdued | light #707070 / dark #c4c4c4 | Texto complementario. |
| ForegroundColorOnPrimary | light #ffffff / dark #1a1a1a | Texto combinado con el relleno principal. |
| ForegroundColorPrimary | light #0265dc / dark #70b1ff | Primer plano interactivo. |
| BorderColorSubdued | light #e6e6e6 / dark #404040 | Límites de bajo énfasis. |
| BorderColorPrimary | light #0265dc / dark #9ecaff | Límite interactivo. |
| StatusColorInfo | light #0265dc / dark #70b1ff | Estado informativo. |
| StatusColorDanger | light #d62100 / dark #ff5b3d | Requiere atención inmediata. |
| StatusColorSuccess | light #077e50 / dark #0bd085 | Revisión local completada. |
| StatusColorWarning | light #ffbe00 / dark #ffe278 | Precaución no crítica. |
### Geometría primitiva y valores adaptables

| Nombre / Regla | Valor / Configuración | Uso |
| --- | --- | --- |
| BreakpointSm | 640px | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| BreakpointMd | 768px | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| BreakpointLg | 1024px | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| BreakpointXl | 1280px | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| BreakpointXxl | 1536px | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| DurationDefault | 200ms | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| DurationInstant | 0ms | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| DurationFast | 100ms | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| DurationSlow | 300ms | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| Radius0 | 0rem | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| Radius1 | 0.1875rem | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| Radius2 | 0.375rem | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| Radius3 | 0.5625rem | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| Radius4 | 0.75rem | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| Radius5 | 0.9375rem | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| Radius6 | 1.125rem | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| Radius7 | 1.3125rem | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
| Radius8 | 1.5rem | Primitiva publicada; los alias semánticos o de componentes determinan las funciones reales de la aplicación. |
### Contrato de anulación de ThemeProvider

| Nombre / Regla | Valor / Configuración | Uso |
| --- | --- | --- |
| theme.semantic | semantic token overrides | Cambia una función como BackgroundColorPrimary. |
| theme.component | component token overrides | Limita un cambio a la función de un componente, por ejemplo, ButtonPrimaryBackgroundColor. |
| value | default/light token value | Proporciona el valor para la apariencia clara. |
| extensions.appearance.dark.value | dark appearance value | Especifica el valor correspondiente para la apariencia oscura. |
| CSS output | --a2-<kebab-case-name> | ThemeProvider serializa la clave del token en una variable CSS. |
| Primitive cascade | resolved at build time | Anular una primitiva en tiempo de ejecución no vuelve a calcular los alias semánticos. |
## Guía completa del sistema de diseño ServiceTitan Anvil

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

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

## Arquitectura de tokens en tres niveles del sistema de diseño ServiceTitan Anvil

### Mantén las primitivas por debajo de las funciones semánticas

> Anvil2 separa las primitivas sin procesar, las funciones semánticas y los alias específicos de componentes. Por lo general, un producto debería elegir la función que describa el significado de la interfaz, en lugar de copiar un azul primitivo en cada control. La tabla de referencia incluye los valores exportados exactos y sus equivalentes para el modo oscuro, de modo que se pueda revisar el tema como un par.

### No des por sentado que los alias se recalculan en tiempo de ejecución

La guía oficial de tematización indica que las referencias a primitivas se resuelven durante la compilación. Cambiar una primitiva mediante un proveedor en tiempo de ejecución no vuelve a generar automáticamente la capa semántica. Para cambiar los colores de la aplicación, anula la función semántica pertinente; para cambiar un componente, usa el nivel de componente. Así se evita que el tema cambie solo en parte y que los controles relacionados queden descoordinados.

## Apariencia clara y oscura del sistema de diseño ServiceTitan Anvil

### Combina los cambios de primer plano y de fondo

La acción principal usa BackgroundColorPrimary con ForegroundColorOnPrimary. En la apariencia clara, es azul con texto blanco; en la oscura, es un azul más claro con una etiqueta oscura. Cambiar solo el relleno daría lugar a una combinación incoherente. El estudio completo de HTML usa light-dark() con un selector color-scheme para mostrar esos valores, sin afirmar que sea ThemeProvider.

### Asigna un significado explícito a los tonos de estado

Anvil2 asigna significados de estado a familias de colores. La revisión local usa una función de éxito, pero aun así indica con precisión lo que ocurrió. El técnico no disponible se identifica mediante texto y tiene un botón de opción deshabilitado. No se usa un matiz verde para insinuar que se ha enviado una visita de servicio; el ejemplo nunca se conecta a un backend de despacho.

## Asignación de servicios y estados del sistema de diseño ServiceTitan Anvil

### Haz que la disponibilidad determine la siguiente opción

Al seleccionar un técnico, las opciones de hora de inicio se actualizan para mostrar su disponibilidad ficticia. La persona no disponible sigue visible, pero no se puede seleccionar. Como se trata de una dependencia entre dos controles, al cambiar el primero, el segundo debe restablecerse a un valor válido, en lugar de conservar una hora que corresponda a otra persona.

### Separa la planificación de la ejecución

La revisión de la asignación registra un plan local e indica explícitamente que no se ha enviado ninguna visita. Un flujo de trabajo de producción necesitaría comprobar los permisos, obtener una respuesta actualizada del horario y gestionar los conflictos antes de confirmar la asignación. El ejemplo mantiene visible ese límite y, a la vez, ofrece una interacción completa que se puede probar y adaptar.

## Densidad operativa y legibilidad del sistema de diseño ServiceTitan Anvil

### Mantén el contexto del trabajo cerca de la selección

Un resumen compacto del trabajo incluye la tarea, la duración, la zona y la prioridad. Las opciones de técnico forman una lista vertical fácil de leer, y la visita propuesta aparece en un panel independiente. La interfaz usa separadores neutros y un color de acento discreto para que sea fácil identificar la selección y la acción principal. Las barras del horario están etiquetadas como ilustrativas; el selector de hora es la fuente de verdad.

### Conserva el orden de lectura en móviles

Por debajo de 768px, la visita propuesta aparece después del trabajo y la lista de técnicos. Los controles mantienen su tamaño completo y las etiquetas se ajustan de forma natural. La vista previa no usa paneles con una altura de ventana fija, por lo que no genera una gran zona vacía debajo de contenido breve. El orden en una sola columna sigue la misma secuencia de decisiones que el diseño más ancho.

## Integración nativa del sistema de diseño ServiceTitan Anvil y alternativa completa HTML

### Usa la estructura documentada del proveedor

ThemeProvider de Anvil2 acepta sobrescrituras de tokens semánticos y de componentes, con extensiones para los valores y la apariencia oscura. La guía incluye a continuación un ejemplo completo del proveedor, basado en la documentación pública. Es una referencia de integración nativa, distinta de la muestra independiente de HTML que se ejecutó localmente.

### Evita afirmar que un paquete se ejecuta si no se ha verificado

Esta página ofrece una implementación funcional de HTML y datos de tokens verificados. El ejemplo del proveedor nativo es una referencia de integración aparte, no el entorno de ejecución que se muestra en la vista previa. Al adoptar componentes de React, instala una versión de Anvil2 compatible, incluye los estilos requeridos y verifica las API de los componentes antes de sustituir los controles nativos de HTML.

## Accesibilidad y verificación de estados del sistema de diseño ServiceTitan Anvil

### Usa semántica nativa de formularios

Los técnicos se presentan como botones de opción nativos con etiquetas, la hora de inicio es un selector con etiqueta y el control de apariencia expone aria-pressed. El mensaje de revisión es una región de estado. El modelo de teclado nativo del navegador gestiona la selección de opciones; el estilo de foco visible es independiente de los estilos de selección y al pasar el cursor.

### Comprueba los cambios después de confirmar

Cambiar de técnico o de hora borra los mensajes de revisión anteriores. Cambiar la apariencia conserva el técnico y la hora seleccionados, porque un cambio visual de tema no debería borrar un plan. Las pruebas deben cubrir ambas transiciones, además de la selección deshabilitada y el desbordamiento en pantallas estrechas, en vez de limitarse a comprobar que se puede hacer clic en un botón.

## Traspaso e implementación del agente para el sistema de diseño ServiceTitan Anvil

### Empieza con el modelo de estado completo

Guarda y ejecuta el ejemplo completo de HTML para inspeccionar su comportamiento. Los pares de valores claros y oscuros derivados de tokens aparecen en la parte superior de CSS, y los datos de disponibilidad definidos aparecen en el script. Una implementación nativa de React puede mantener las mismas transiciones de estado y sustituir los controles de Anvil2 tras verificarlos.

1. Ejecuta la muestra completa de HTML.

2. Selecciona al segundo técnico y verifica que cambien las opciones de hora de inicio.

3. Revisa el plan, cambia la hora y verifica que se borre el mensaje.

4. Cambia la apariencia y verifica que se conserve la selección actual.

### Especifica qué requiere datos reales

Pide al agente que conserve la lista de horas dependiente del técnico, el miembro del personal deshabilitado, la invalidación de la revisión y el comportamiento de la apariencia. Sustituye el trabajo y el personal ficticios solo cuando haya un contrato de datos real. No conviertas la revisión local en una afirmación de éxito simplemente cambiando el texto del botón.

- Comprueba la disponibilidad antes de asignar un servicio real.

- Mantén separadas las sobrescrituras semánticas y las de componentes.

- Prueba los pares de primer plano y fondo en los modos claro y oscuro.

## Ejemplos de componentes del sistema de diseño ServiceTitan Anvil

### Muestra completa de asignación local

HTML/CSS/JavaScript originales, con apariencias derivadas de tokens y estado de formulario dependiente.

```
<!doctype html>
<html lang="en">
  <meta charset="utf-8" /><meta
    name="viewport"
    content="width=device-width,initial-scale=1"
  /><title>Dispatch assignment study</title
  ><style>
    :root {
      color-scheme: light;
      --surface: light-dark(#fff, #1a1a1a);
      --hover: light-dark(#f9f9f9, #292929);
      --text: light-dark(#040404, #fff);
      --muted: light-dark(#707070, #c4c4c4);
      --line: light-dark(#e6e6e6, #404040);
      --action: light-dark(#0265dc, #70b1ff);
      --on-action: light-dark(#fff, #1a1a1a);
      --faint: light-dark(#fafcff, #000b19);
      --success: light-dark(#077e50, #0bd085);
    }
    * {
      box-sizing: border-box;
    }
    body {
      margin: 0;
      padding: 24px;
      background: var(--surface);
      color: var(--text);
      font:
        16px/1.5 system-ui,
        sans-serif;
    }
    h1 {
      font-size: 28px;
      margin: 8px 0;
    }
    h2 {
      font-size: 20px;
      margin: 0 0 16px;
    }
    p {
      margin: 8px 0 16px;
    }
    .small {
      font-size: 13px;
      color: var(--muted);
    }
    header {
      display: flex;
      justify-content: space-between;
      gap: 24px;
      border-bottom: 1px solid var(--line);
      padding-bottom: 24px;
    }
    button,
    select {
      font: inherit;
      color: inherit;
      background: var(--surface);
      border: 1px solid var(--line);
      border-radius: 4px;
      padding: 10px 16px;
      min-height: 44px;
    }
    button {
      cursor: pointer;
    }
    button:hover {
      background: var(--hover);
    }
    button:focus-visible,
    select:focus-visible,
    input:focus-visible {
      outline: 3px solid var(--action);
      outline-offset: 3px;
    }
    main {
      display: grid;
      grid-template-columns: 1.4fr 1fr;
      gap: 24px;
      margin-top: 24px;
    }
    .board {
      border: 1px solid var(--line);
      border-radius: 8px;
      padding: 24px;
    }
    .job {
      padding: 16px;
      background: var(--faint);
      border-left: 4px solid var(--action);
      margin-bottom: 24px;
    }
    .job strong {
      display: block;
    }
    .people {
      display: grid;
      gap: 12px;
    }
    .person {
      display: grid;
      grid-template-columns: 24px 1fr auto;
      gap: 12px;
      padding: 16px;
      border: 1px solid var(--line);
      border-radius: 4px;
      align-items: center;
    }
    .person:has(:checked) {
      border-color: var(--action);
      background: var(--faint);
    }
    input {
      accent-color: var(--action);
      width: 18px;
      height: 18px;
    }
    .person small {
      display: block;
      color: var(--muted);
    }
    .person:has(:disabled) {
      background: var(--hover);
    }
    .timeline {
      display: flex;
      gap: 4px;
      margin: 20px 0;
    }
    .timeline span {
      height: 40px;
      flex: 1;
      background: var(--line);
      border-radius: 4px;
    }
    .timeline .open {
      background: var(--action);
    }
    .timeline-label {
      display: flex;
      justify-content: space-between;
      font-size: 13px;
      color: var(--muted);
    }
    label[for="time"] {
      display: block;
      margin: 16px 0 8px;
    }
    .primary {
      background: var(--action);
      color: var(--on-action);
      border-color: var(--action);
      margin-top: 24px;
    }
    .primary:hover {
      filter: brightness(0.95);
      background: var(--action);
    }
    #feedback {
      color: var(--success);
      padding-top: 16px;
    }
    footer {
      border-top: 1px solid var(--line);
      padding-top: 16px;
      margin-top: 24px;
      font-size: 13px;
      color: var(--muted);
    }
    @media (max-width: 767px) {
      body {
        padding: 16px;
      }
      main {
        grid-template-columns: 1fr;
      }
      .board {
        padding: 16px;
      }
      header {
        display: block;
      }
      header button {
        margin-top: 8px;
      }
      .person {
        grid-template-columns: 24px 1fr;
      }
      .person > span:last-child {
        grid-column: 2;
      }
    }
  </style>
  <body>
    <header>
      <div>
        <div class="small">SERVICE OPERATIONS / LOCAL PLANNING STUDY</div>
        <h1>Assign a service visit</h1>
        <p>Match a sample job with an available technician.</p>
      </div>
      <div>
        <button id="theme" aria-pressed="false">Dark appearance</button>
      </div>
    </header>
    <main>
      <section class="board">
        <h2>Job and technician</h2>
        <div class="job">
          <strong>Sample job #204 · Heating inspection</strong
          ><span>90 minutes · North district · Routine priority</span>
        </div>
        <div class="people" id="people"></div>
      </section>
      <aside class="board">
        <h2>Proposed visit</h2>
        <p id="technician"></p>
        <label for="time">Available start time</label
        ><select id="time"></select>
        <div class="timeline" aria-hidden="true">
          <span></span><span class="open"></span><span class="open"></span
          ><span></span><span></span><span></span>
        </div>
        <div class="timeline-label">
          <span>08:00</span><span>Illustrative day</span><span>17:00</span>
        </div>
        <p class="small">
          The time list is the source of available choices. The bars are a
          decorative schedule summary.
        </p>
        <button id="assign" class="primary">Review assignment</button>
        <p id="feedback" role="status"></p>
      </aside>
    </main>
    <footer>
      Original HTML using Anvil2 Hammer token 3.5.0 light/dark roles. Fictional
      technicians and schedules; no dispatch service connected.
    </footer>
    <script>
      const staff = [
        {
          id: "alex",
          name: "Alex Morgan",
          skill: "Heating specialist",
          slots: ["09:00", "11:00"],
        },
        {
          id: "sam",
          name: "Sam Rivera",
          skill: "General maintenance",
          slots: ["13:00", "15:00"],
        },
        { id: "lee", name: "Lee Chen", skill: "Unavailable today", slots: [] },
      ];
      let selected = "alex";
      const $ = (id) => document.getElementById(id);
      function update() {
        const p = staff.find((s) => s.id === selected);
        $("technician").textContent = p.name + " · " + p.skill;
        $("time").replaceChildren(...p.slots.map((t) => new Option(t, t)));
        $("feedback").textContent = "";
      }
      $("people").replaceChildren(
        ...staff.map((p) => {
          const l = document.createElement("label");
          l.className = "person";
          const input = document.createElement("input");
          input.type = "radio";
          input.name = "technician";
          input.checked = p.id === selected;
          input.disabled = p.slots.length === 0;
          input.onchange = () => {
            selected = p.id;
            update();
          };
          const text = document.createElement("span");
          text.innerHTML = `<strong>${p.name}</strong><small>${p.skill}</small>`;
          const availability = document.createElement("span");
          availability.textContent = p.slots.length
            ? p.slots.length + " start times"
            : "Unavailable";
          l.append(input, text, availability);
          return l;
        }),
      );
      $("time").onchange = () => {
        $("feedback").textContent = "";
      };
      $("assign").onclick = () => {
        $("feedback").textContent =
          "Local plan: " +
          staff.find((s) => s.id === selected).name +
          " at " +
          $("time").value +
          ". No visit has been dispatched.";
      };
      $("theme").onclick = () => {
        let dark = $("theme").getAttribute("aria-pressed") !== "true";
        $("theme").setAttribute("aria-pressed", dark);
        document.documentElement.style.colorScheme = dark ? "dark" : "light";
      };
      update();
    </script>
  </body>
</html>
```

Referencia de código

- Guárdalo como index.html; los navegadores modernos admiten light-dark(). Este no es el entorno de ejecución nativo de Anvil2.

### Referencia de integración de ThemeProvider

Estructura documentada de sobrescritura semántica en una entrada completa de React. Valídala con la versión instalada de Anvil2.

```
import React from 'react';
import { createRoot } from 'react-dom/client';
import { ThemeProvider } from '@servicetitan/anvil2';

const theme = {
  semantic: {
    BackgroundColorPrimary: {
      value: '#7c3aed',
      extensions: { appearance: { dark: { value: '#a78bfa' } } },
    },
  },
};

function App() {
  return (
    <ThemeProvider theme={theme}>
      <main>
        <h1>Theme integration check</h1>
        <div style={{ background: 'var(--a2-background-color-primary)', padding: 24 }}>
          <span style={{ color: 'var(--a2-foreground-color-on-primary)' }}>
            Primary role with the provider’s matching foreground role
          </span>
        </div>
      </main>
    </ThemeProvider>
  );
}
createRoot(document.getElementById('root')).render(<App />);
```

Referencia de código

- Esta referencia del proveedor se verificó con la documentación oficial; la muestra en ejecución de arriba es una implementación independiente de HTML.

## Configuración del agente para el sistema de diseño ServiceTitan Anvil

### Punto de partida para la implementación

La muestra ejecutada no requiere un paquete de Anvil2. El ejemplo independiente de ThemeProvider requiere una instalación nativa compatible.

```
# Save the complete HTML example as index.html
python3 -m http.server 8080
# Open http://localhost:8080
```

### Una instrucción para tu proyecto

```
Crea una interfaz de asignación de servicios con roles semánticos de Anvil2. Conserva los pares de primer plano y fondo para los modos claro y oscuro, las opciones de hora dependientes del técnico, el personal no disponible deshabilitado y la invalidación de la revisión del ejemplo completo. Elige explícitamente entre HTML original y una instalación de React de Anvil2 verificada. Nunca afirmes que se ha asignado un servicio sin una respuesta del backend.
```

## Fuentes y alcance del sistema de diseño ServiceTitan Anvil

Una guía de OpenDesign para ServiceTitan Anvil2. Los valores de los tokens se extraen de Hammer token 3.5.0 público. El ejemplo en ejecución está hecho con HTML original, no se ejecuta con React de Anvil2; todos los técnicos y datos de disponibilidad son ficticios.

- Tematización Niveles de tokens, sobrescrituras y valores de apariencia.

- Colores Roles de estado y orientación sobre el color de interacción.

- Espaciado Escala de espaciado e integración de utilidades.

- Tamaños Fundamentos de tamaños.

- Arquitectura de la biblioteca Paquetes de Anvil2 y límites de la biblioteca.

## Recursos relacionados con el sistema de diseño ServiceTitan Anvil

### Siemens iX

Inspecciona eventos industriales con un vocabulario de severidad explícito, roles de color adaptados al tema y iX Event List API. Esta guía crea un cuaderno local de equipos sin confundir un registro revisado con una alarma de máquina resuelta.

### Sprout Social Seeds

Inspecciona los tokens públicos reales de Seeds para color, espaciado, tipografía de producto y bordes mediante una cola editorial completa, con selección múltiple, estados vacíos con filtros y opción para deshacer.

### SumUp Circuit

Una guía de Circuit con tokens semánticos exactos, propiedades de validación nativas, un editor completo de artículos para comerciantes y una vista previa ilustrada para clientes.


- [Referencia de implementación](https://anvil.servicetitan.com/docs/web/foundations/theming)
- [Fuente](https://anvil.servicetitan.com/docs/web/foundations/theming)
- [Fuente](https://anvil.servicetitan.com/docs/web/foundations/sizing)
- [Referencia de código](https://anvil.servicetitan.com/docs/web/foundations/theming)
- [Tematización Niveles de tokens, sobrescrituras y valores de apariencia.](https://anvil.servicetitan.com/docs/web/foundations/theming)
- [Colores Roles de estado y orientación sobre el color de interacción.](https://anvil.servicetitan.com/docs/web/foundations/colors)
- [Espaciado Escala de espaciado e integración de utilidades.](https://anvil.servicetitan.com/docs/web/foundations/spacing)
- [Tamaños Fundamentos de tamaños.](https://anvil.servicetitan.com/docs/web/foundations/sizing)
- [Arquitectura de la biblioteca Paquetes de Anvil2 y límites de la biblioteca.](https://anvil.servicetitan.com/docs/library-architecture)
