7. PWA con formulario y Material Design 3 Expressive

Versión para imprimir.

A. Introduccion

B. Referencias

Sitios de Material Design
Material.io

Material Design es una guía de diseño para aplicaciones multiplataforma. La encuentras en https://m3.material.io/.

Herramienta para selección de colores
Material Theme Builder

https://material-foundation.github.io/material-theme-builder/

Adaptación multiplataforma

La forma de adaptar Material Design en distintas plataformas está en https://material.io/design/platform-guidance/cross-platform-adaptation.html

C. Hazlo funcionar (con videos)

  1. Prueba e instala, de preferencia con Chrome, el sitio https://pwamdex.web.app/.

  2. Descarga el archivo /src/pwaform.zip y descompáctalo.

  3. Crea una cuenta de email de Google por ejemplo, juanito@google.com

  4. En https://console.firebase.google.com/ selecciona la cuenta de email que acabas de crear, por ejemplo, juanito@google.com

  5. Crea un proyecto de Firebase nuevo. Por ejemplo, pwamdex. No uses IA ni Amalytics.

  6. Crea una cuenta de GitHub usando el email anterior y selecciona el nombre de usuario unsando la parte inicial del correo electrónico, por ejemplo juanito.

  7. Crea un repositorio nuevo. En el nombre del repositorio, pon el nombre del proyecto de firebase, por ejemplo pwamdex

  8. Importa el proyecto de GitHub a Visual Studio Code

  9. En Visual Studio Code selecciona el menú Terminal > New Terminal y al abrirse esta, teclea cmd y luego ENTER

  10. En la consola de firebase, en el costado izquierdo, en la opción Hosting y sin servidores selecciona Hosting. Añade hosting a ti sitio (Sin usar IA ni Amalytics) y sigue las instrucciones que se indican para para la consola, que básicamente son

    1. firebase login
      Si no has iniciado sesión, se abre el navegador y tienes que entrar a sesión con tu cuenta de Firebase.
    2. firebase init
      Selecciona el proyecto que ya está creado en Firebase, solo ponle hosting y emulador de hosting (selecciona que lo descargue.) Selecciona que no sea una Single page app.
  11. Edita los archivos que desees.

  12. Crea los íconos del proyecto con https://www.photopea.com/.

  13. Crea los íconos enmascarables con https://maskable.app/ a partir del archivo «icono2048.png».

  14. Coloca el archivo favicon.ico en la carpeta /public del proyecto.

  15. Coloca los otros íconos en la carpeta /public/img y asegúrate de que estén declarados en el archivo /public/site.webmanifest.

  16. El proyecto usa algunas librerías ya existentes para realizar algunas partes del proyecto. Ya están incluidos en el archivo zip, pero a continuación se indica como se incluyeron.

    Ungap custom elements
    Se descarga el archivo es.js del repositorio https://github.com/ungap/custom-elements y se coloca en la carpeta /public/ungap del proyecto
    material-tokens
    Se descarga el repositorio https://github.com/material-foundation/material-tokens y copia el contenido de la cerpeta css a la carpeta /public/material-tokens/css del proyecto, sustituyendo todo el contenido de esta última carpeta. A los temas les faltan algunos tokens más nuevos, por lo que debes hacer el siguiente paso.
  17. Para cambiar los colores, entra al sitio https://material-foundation.github.io/material-theme-builder/, selecciona los colores de tu aplicación, haz clic en Pick your fonts →. Elige font Roboto, haz clic en Export theme →. Haz clic en Export y selecciona Web (CSS) para descargar el zip que contiene los estilos que generan los colores. Descompacta el zip y copia los archivos de la carpeta css a la carpeta /public/material-tokens/css del proyecto, sobreescribiendo los archivos dark.css, para el tema osciro y light.css para el tema claro.

  18. El archivo /public/sw.js tiene una lista de los archivos que se instalan. Puedes generar la lista abriendo una terminal en Visual Studio Code e introducir la orden
    node generar-listado-sw.js
    se genera el archivo /lista_archivos_sw.txt con el listado que se puede copiar al archivo /public/sw.js.

  19. Para depurar localmente tu aplicación, teclea en la terminal
    firebase emulators:start
    Lo puedes ver en la dirección http://localhost:5000. Para detener el servidor local, en la terminal debes presionar Ctrl+C En el siguiente enlace se muestra como probar la aplicación. La forma de levantar el servidor es diferente, pero la forma de usar el navegador es la misma. Prueba tu PWA.

  20. Cada vez que vayas a publicar cambios, debes modificar el valor de VERSION en el archivo /public/sw.js para poder ver los cambios en el navegador.

  21. Cuando desarrolles, es incómodo modificar la versión cada que realizas cambios; en vez de ello desinstala la app:

    1. Abre las herramientas de depuración haciendo clic derecho en la página y selecciona Inspeccionar (o Inspect si aparece en inglés).

    2. En la Pestaña Aplicación (o Application en inglés) selecciona Almacenamoento (o Storage en inglés). Cliquea Borrar datos del sitio.

    3. Recarga la app, de preferencia haciendo clic derecho en el ícono de volver a cargar la página Ïmagen del ícono de recarga y seleccionando vaciar caché y volver a cargar de manera forzada (o algo parecido). Si no aparece un menú emergente, simplemente cliquea volver a cargar la página Ïmagen del ícono de recarga. Revisa que no aparezca ningún error ni en la pestañas Consola, ni en Red.

    4. Tanbién puedes usar la combinación de teclas Ctrl+Mayúsculas+r para forzar que se actualice temporalmente el navegador en caso de que no se vean los cambios.

    5. En la Pestaña Aplicación (o Application en inglés) selecciona Archivo de manifiesto (o Manifest file en inglés). Esta herramienta analiza la estructura del archivo de manifiesto y te indica si hay un error.

    6. En la Pestaña Aplicación (o Application en inglés) selecciona Almacenamiento en caché (o Cache storage en inglés). Aquí puedes revisar si el caché de la aplicación se llenó correctamente. En caso de que esté vacío, es que hubo algún error durante la carga y la app se ejecuta más lenta.

    7. El archivo .htaccess Solo se utiliza en servidores compatibles con apache, como Ngnx o infinity free. Sirve para quitar el cache de http y declarar el mime type de los archivos .manifest

    8. Para publicar tu aplicación, teclea en la terminal
      firebase deploy
      Te va a indicar la dirección para ver tu sitio en internet.

    9. Si quieres cerrar tu sesión de deasarrollo, teclea en la terminal: firebase logout
    10. Instala y usa tu PWA en Windows. Aunque en este video se recomienda usar Edge, al momento de actualizar el contenido, la opción más recomendada es Chrome para que te muestre las descripciones y las capturas de pantalla.

    11. Instala y usa tu PWA en Android. Al momento de actualizar las notas, tal vez no te aparezca el botón para instalar y tengas que seleccionar la acción de agregar a la pantalla principal que aparece en el menú de extensión de Chrome.

    12. Instala y usa tu PWA en iOS (iPhone y iPad).

D. Archivos

Haz clic en los triángulos para expandir las carpetas

E. Carpeta « public »

Versión para imprimir.

A. public / .htaccess

  • Este archivo configura las respuestas del servidor..

  • Lo utilizan principalmente servidores como Apache o Nginx.

  • Configura el mime type para el archivo de manifiesto y sedhabilita el uso de la cache general de http..

1
AddType application/manifest+json .webmanifest
2
3
ExpiresActive On
4
5
Header set Cache-Control "max-age=1, must-revalidate"
6

B. public / 404.html

1
<!DOCTYPE html>
2
<html>
3
  <head>
4
    <meta charset="utf-8">
5
    <meta name="viewport" content="width=device-width, initial-scale=1">
6
    <title>Page Not Found</title>
7
8
    <style media="screen">
9
      body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; }
10
      #message { background: white; max-width: 360px; margin: 100px auto 16px; padding: 32px 24px 16px; border-radius: 3px; }
11
      #message h3 { color: #888; font-weight: normal; font-size: 16px; margin: 16px 0 12px; }
12
      #message h2 { color: #ffa100; font-weight: bold; font-size: 16px; margin: 0 0 8px; }
13
      #message h1 { font-size: 22px; font-weight: 300; color: rgba(0,0,0,0.6); margin: 0 0 16px;}
14
      #message p { line-height: 140%; margin: 16px 0 24px; font-size: 14px; }
15
      #message a { display: block; text-align: center; background: #039be5; text-transform: uppercase; text-decoration: none; color: white; padding: 16px; border-radius: 4px; }
16
      #message, #message a { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); }
17
      #load { color: rgba(0,0,0,0.4); text-align: center; font-size: 13px; }
18
      @media (max-width: 600px) {
19
        body, #message { margin-top: 0; background: white; box-shadow: none; }
20
        body { border-top: 16px solid #ffa100; }
21
      }
22
    </style>
23
  </head>
24
  <body>
25
    <div id="message">
26
      <h2>404</h2>
27
      <h1>Page Not Found</h1>
28
      <p>The specified file was not found on this website. Please check the URL for mistakes and try again.</p>
29
      <h3>Why am I seeing this?</h3>
30
      <p>This page was generated by the Firebase Command-Line Interface. To modify it, edit the <code>404.html</code> file in your project's configured <code>public</code> directory.</p>
31
    </div>
32
  </body>
33
</html>
34

C. public / ayuda.html

1
<!DOCTYPE html>
2
<html lang="es" class="light dark">
3
4
<head>
5
6
 <meta charset="UTF-8">
7
8
 <title>Ayuda - PWA con Formulario</title>
9
10
 <script type="module" src="js/registraServiceWorker.js"></script>
11
 <script type="module" src="libclienteweb/manejaErrores.js"></script>
12
 
13
 <meta name="viewport" content="width=device-width">
14
 <meta name="theme-color" content="#fffbfe">
15
 <link rel="icon" sizes="32x32" href="favicon.ico">
16
 <link rel="manifest" href="site.webmanifest">
17
 <link rel="stylesheet" href="css/material-symbols-outlined.css">
18
 <link rel="stylesheet" href="material-tokens/css/baseline.css">
19
 <link rel="stylesheet" href="css/estilos.css">
20
 <script src="ungap/es.js"></script>
21
22
 <script type="module" src="libmde/md-app-bar.js"></script>
23
 <script type="module" src="js/nav-tab-fixed.js"></script>
24
25
 <link rel="stylesheet" href="libmde/md-tab.css">
26
 <link rel="stylesheet" href="libmde/md-list.css">
27
 <link rel="stylesheet" href="css/transicion_pestanas.css">
28
29
</head>
30
31
<body>
32
33
 <md-app-bar adicional="tab">
34
35
  <h1>Ayuda</h1>
36
37
 </md-app-bar>
38
39
 <nav-tab-fixed id="tab"></nav-tab-fixed>
40
41
 <section>
42
43
  <ul class="md-list">
44
   <li class="md-two-line">
45
    <span class="headline">
46
     Título
47
    </span>
48
    <span class="supporting">
49
     PWA con Material Design
50
    </span>
51
   </li>
52
   <li class="md-two-line">
53
    <span class="headline">
54
     Descripción
55
    </span>
56
    <span class="supporting">
57
     Ejemplos de vistas móviles con formularios.
58
    </span>
59
   </li>
60
   <li class="md-two-line">
61
    <span class="headline">
62
     Autor
63
    </span>
64
    <span class="supporting">
65
     Gilberto Pacheco Gallegos
66
    </span>
67
   </li>
68
   <li class="md-two-line">
69
    <span class="headline">
70
     Derechos de autor
71
    </span>
72
    <span class="supporting">
73
     © 2025 Gilberto Pacheco Gallegos
74
    </span>
75
   </li>
76
   <li class="md-three-line">
77
    <span class="headline">
78
     Este software usa las librerías libclienteweb y libmde.
79
     </span>
80
     <span class="supporting">
81
      Estas obras de Gilberto Pacheco Gallegos están bajo una
82
     <a target="_blank" rel="license noreferrer"
83
      href="http://creativecommons.org/licenses/by/4.0/">
84
      Licencia Creative Commons Atribución 4.0 Internacional</a></span>
85
   </li>
86
   <li>
87
    <a class="md-three-line" target="_blank" rel=”noreferrer”
88
     href="https://fonts.google.com/icons">
89
     <span class="headline">
90
      También usa Material Symbols
91
     </span>
92
     <span class="supporting">
93
      Desarrollada por Google bajo licencia Apache 2.0
94
     </span>
95
    </a>
96
   </li>
97
   <li>
98
    <a class="md-three-line" target="_blank" rel=”noreferrer”
99
     href="https://github.com/material-foundation/material-tokens">
100
     <span class="headline">
101
      También usa Material Tokens
102
     </span>
103
     <span class="supporting">
104
      Desarrollada por Google bajo licencia Apache 2.0
105
     </span>
106
    </a>
107
   </li>
108
   <li>
109
    <a class="md-three-line" target="_blank" rel=”noreferrer”
110
     href="https://github.com/ungap/custom-elements">
111
     <span class="headline">
112
      También usa Custom Elements Polyfill
113
     </span>
114
     <span class="supporting">
115
      Desarrollada por ungap bajo licencia ISC
116
     </span>
117
    </a>
118
   </li>
119
  </ul>
120
121
 </section>
122
123
 <nav-drw></nav-drw>
124
125
</body>
126
127
</html>

D. public / favicon.ico

favicon.ico

E. public / index.html

1
<!DOCTYPE html>
2
<html lang="es" class="light dark">
3
4
<head>
5
6
 <meta charset="UTF-8">
7
8
 <title>PWA con Formulario</title>
9
10
 <meta name="description" content="Ejemplo de PWA con Formulario">
11
12
 <script type="module" src="js/registraServiceWorker.js"></script>
13
 <script type="module" src="libclienteweb/manejaErrores.js"></script>
14
15
 <meta name="viewport" content="width=device-width">
16
 <meta name="theme-color" content="#fffbfe">
17
 <link rel="icon" sizes="32x32" href="favicon.ico">
18
 <link rel="manifest" href="site.webmanifest">
19
 <link rel="stylesheet" href="css/material-symbols-outlined.css">
20
 <link rel="stylesheet" href="material-tokens/css/baseline.css">
21
 <link rel="stylesheet" href="css/estilos.css">
22
 <script src="ungap/es.js"></script>
23
24
 <script type="module" src="libmde/md-app-bar.js"></script>
25
 <script type="module" src="js/nav-tab-fixed.js"></script>
26
27
 <link rel="stylesheet" href="libmde/md-tab.css">
28
 <link rel="stylesheet" href="libmde/md-filled-text-field.css">
29
 <link rel="stylesheet" href="libmde/md-filled-button.css">
30
 <link rel="stylesheet" href="libmde/md-outline-button.css">
31
 <link rel="stylesheet" href="css/transicion_pestanas.css">
32
33
</head>
34
35
<body>
36
37
 <form id="formulario" novalidate>
38
39
  <md-app-bar class="centered" adicional="tab">
40
41
   <h1>PWA con Formulario</h1>
42
43
  </md-app-bar>
44
45
  <nav-tab-fixed id="tab"></nav-tab-fixed>
46
47
  <section>
48
49
   <!-- Usa
50
    class="float"
51
   cuando quieras que la etiqueta esté arriba todo el tiempo. -->
52
   <p>
53
    <label class="md-filled-text-field float">
54
     <output name="respuesta">Todavía no hay respuesta</output>
55
     <span>Respuesta</span>
56
    </label>
57
   </p>
58
59
   <p>
60
    <label class="md-filled-text-field">
61
     <input name="nombre" required placeholder="Nombre*">
62
     <span>Nombre *</span>
63
     <small id="supportingNombre">Obligatorio</small>
64
    </label>
65
   </p>
66
67
   <p>
68
    <label class="md-filled-text-field">
69
     <input name="email" type="email" placeholder="Email">
70
     <span accesskey="M">Email</span>
71
     <small id="supportingEmail">Texto con formato de email</small>
72
    </label>
73
   </p>
74
75
   <p>
76
    <label class="md-filled-text-field float">
77
     <input name="fecha" type="date" placeholder="Fecha">
78
     <span>Fecha de nacimiento</span>
79
    </label>
80
   </p>
81
82
   <p>
83
    <label class="md-filled-text-field">
84
     <textarea name="dieccion" rows="3" placeholder="Dirección"></textarea>
85
     <span>Dirección</span>
86
    </label>
87
   </p>
88
89
   <p>
90
    <button id="botonSaludo" class="md-filled-button">Saludar</button>
91
    <button id="botonDatos" class="md-outline-button">Datos</button>
92
   </p>
93
94
  </section>
95
96
 </form>
97
98
 <script type="module">
99
100
  import { muestraTextoDeAyuda } from "./libclienteweb/muestraTextoDeAyuda.js"
101
102
  formulario.nombre.addEventListener("input", copiaMensajes)
103
  formulario.email.addEventListener("input", copiaMensajes)
104
  formulario.addEventListener("submit", procesa)
105
106
  function copiaMensajes() {
107
   muestraTextoDeAyuda(formulario.nombre, supportingNombre, "Obligatorio")
108
   muestraTextoDeAyuda(
109
    formulario.email, supportingEmail, "Texto con formato de email"
110
   )
111
  }
112
113
  /**
114
   * @param {SubmitEvent} evt
115
   */
116
  function procesa(evt) {
117
   evt.preventDefault()
118
   copiaMensajes()
119
   if (
120
    formulario.nombre.validity.valid
121
    && formulario.email.validity.valid
122
    && formulario.fecha.validity.valid
123
    && formulario.dieccion.validity.valid
124
   ) {
125
    const botonSeleccionado = evt.submitter
126
    if (botonSeleccionado === botonSaludo) {
127
     saluda()
128
    } else if (botonSeleccionado === botonDatos) {
129
     datos()
130
    }
131
   }
132
  }
133
134
  function saluda() {
135
   formulario.respuesta.value = `Hola ${formulario.nombre.value}`
136
  }
137
138
  function datos() {
139
   formulario.respuesta.value =
140
    `Nombre: ${formulario.nombre.value} Email: ${formulario.email.value}`
141
  }
142
143
 </script>
144
145
</body>
146
147
</html>

F. public / select.html

1
<!DOCTYPE html>
2
<html lang="es" class="light dark">
3
4
<head>
5
6
 <meta charset="UTF-8">
7
 <title>Select - PWA con Formulario</title>
8
9
 <script type="module" src="js/registraServiceWorker.js"></script>
10
 <script type="module" src="libclienteweb/manejaErrores.js"></script>
11
12
 <meta name="viewport" content="width=device-width">
13
 <meta name="theme-color" content="#fffbfe">
14
 <link rel="icon" sizes="32x32" href="favicon.ico">
15
 <link rel="manifest" href="site.webmanifest">
16
 <link rel="stylesheet" href="css/material-symbols-outlined.css">
17
 <link rel="stylesheet" href="material-tokens/css/baseline.css">
18
 <link rel="stylesheet" href="css/estilos.css">
19
 <script src="ungap/es.js"></script>
20
21
 <script type="module" src="libmde/md-app-bar.js"></script>
22
 <script type="module" src="js/nav-tab-fixed.js"></script>
23
 <script type="module" src="libmde/md-select-menu.js"></script>
24
 <script type="module" src="libmde/md-options-menu.js"></script>
25
26
 <link rel="stylesheet" href="libmde/md-tab.css">
27
 <link rel="stylesheet" href="libmde/md-menu.css">
28
 <link rel="stylesheet" href="libmde/md-filled-text-field.css">
29
 <link rel="stylesheet" href="libmde/md-filled-button.css">
30
 <link rel="stylesheet" href="css/transicion_pestanas.css">
31
32
</head>
33
34
<body>
35
36
 <form id="formulario" novalidate>
37
38
  <md-app-bar adicional="tab">
39
40
   <h1>Select</h1>
41
42
  </md-app-bar>
43
44
  <nav-tab-fixed id="tab"></nav-tab-fixed>
45
46
  <section>
47
48
   <p>
49
    <span id="etiquetaGrupo" class="md-filled-text-field" accesskey="G">
50
     <md-select-menu name="grupo" required value="IC21"
51
      aria-labelledby="etiquetaGrupo"
52
      options="opcionesDeGrupo"></md-select-menu>
53
     <span>Grupo *</span>
54
     <small id="supportingGrupo">Obligatorio</small>
55
    </span>
56
   </p>
57
58
   <p>
59
    <button class="md-filled-button" style="width: 100%;">Enviar</button>
60
   </p>
61
62
  </section>
63
64
  <md-options-menu id="opcionesDeGrupo" aria-label="Opciones de grupo">
65
   <span data-value="" title="Selecciona opción"></span>
66
   <span data-value="IC21">IC-21</span>
67
   <span data-value="IC22">IC-22</span>
68
   <span data-value="IC23">IC-23</span>
69
  </md-options-menu>
70
71
 </form>
72
 <script type="module">
73
74
  import { muestraTextoDeAyuda } from "./libclienteweb/muestraTextoDeAyuda.js"
75
76
  formulario.grupo.addEventListener("input", copiaMensajes)
77
  formulario.addEventListener("submit", procesa)
78
79
  function copiaMensajes() {
80
   muestraTextoDeAyuda(formulario.grupo, supportingGrupo, "Obligatorio")
81
  }
82
83
  /**
84
   * @param {SubmitEvent} evt
85
   */
86
  function procesa(evt) {
87
   copiaMensajes()
88
   if (!formulario.grupo.validity.valid) {
89
    evt.preventDefault()
90
   }
91
  }
92
93
 </script>
94
95
</body>
96
97
</html>

G. public / site.webmanifest

  • Este archivo sirve para configurar los instaladores de la aplicación.

Explicación de las propiedades

short_name

Nombre corto. Normalmente se despliega en dispositivos móviles. Máximo 20 caracteres.

name

Nombre largo. Normalmente se despliega en computadoras de escritorio. Máximo 30 caracteres.

id

Identificador del archivo de instalación. Normalmente es la ruta del archivo inicial de la app.

start_url

Ruta del archivo inicial de la app.

display

Forma de mostrar la app. El término standalone significa que no se muestra la barra de navegación del navegador web.

theme_color

Color de la barra de estado (en dispositivos móviles) o de título (en computadoras de escritorio) de la app.

background_color

Color de fondo de la pantalla desplah en dispositivos móviles.

description

Describe el propósito de la aplicación. Aparece en el cuadro de diálogo que muestra el navegador al instalar la app.

screenshots

Listado de máximo 8 capturas de pantalla. Aparecen en el cuadro de diálogo que muestra el navegador al instalar la app. Debes incluir al menos una con "form_factor": "wide" y otra con "form_factor": "narrow".

icons

Listado de íconos en distintas resoluciones para los instaladores de la app. Se selecciona el que se vea mejor según las característocas del dispositivo.

src

Url de la imagen dentro de la app.

sizes

Dimensiones en pixeles de la imagen, anchoxalto.

type

Tipo mime de la imagen.

purpose

Forma en que se usa la imagen.

maskable

La imagen puede recortarse de forma segura para tomar distintas formas, como círculos, gotas, cuadrados con esquinas redondeadas, etc. Normalmente se usa para dispositivos móviles.

any

No se puede asegurar nada sobre la imagen. Normalmente se usa para dispositivos de escritorio.

Normalmente debe proporcionarse un juego de íconos con purpose any y otro juego de íconos con purpose maskable.

form_factor

Orientación de una screenshot.

wide

La screenshot tiene una orientación horizontal. Normalmente la creenshot se usa para dispositivos de escritorio.

narrow

La screenshot tiene una orientación vertical. Normalmente la creenshot se usa para dispositivos móviles.

Debes incluir al menos una screenshot con "form_factor": "wide" y otra con "form_factor": "narrow".

label

Descripción de una screenshot. Aparece en el cuadro de diálogo que muestra el navegador al instalar la app.

1
{
2
 "short_name": "Formulario PWA",
3
 "name": "PWA con Formulario",
4
 "id": "/index.html",
5
 "start_url": "/index.html",
6
 "display": "standalone",
7
 "theme_color": "#fffbfe",
8
 "background_color": "#fffbfe",
9
 "display_override": [
10
  "window-controls-overlay"
11
 ],
12
 "description": "PWA con componentes de Material Design.",
13
 "screenshots": [
14
  {
15
   "src": "/img/screenshot_horizontal.png",
16
   "sizes": "760x384",
17
   "type": "image/png",
18
   "form_factor": "wide",
19
   "label": "PWA con Material Design"
20
  },
21
  {
22
   "src": "/img/screenshot_vertical.png",
23
   "sizes": "557x759",
24
   "type": "image/png",
25
   "form_factor": "narrow",
26
   "label": "PWA con Material Design (2)"
27
  }
28
 ],
29
 "icons": [
30
  {
31
    "purpose": "maskable",
32
    "sizes": "2730x2730",
33
    "src": "img/maskable_icon.png",
34
    "type": "image/png"
35
  },
36
  {
37
    "purpose": "maskable",
38
    "sizes": "48x48",
39
    "src": "img/maskable_icon_x48.png",
40
    "type": "image/png"
41
  },
42
  {
43
    "purpose": "maskable",
44
    "sizes": "72x72",
45
    "src": "img/maskable_icon_x72.png",
46
    "type": "image/png"
47
  },
48
  {
49
    "purpose": "maskable",
50
    "sizes": "96x96",
51
    "src": "img/maskable_icon_x96.png",
52
    "type": "image/png"
53
  },
54
  {
55
    "purpose": "maskable",
56
    "sizes": "128x128",
57
    "src": "img/maskable_icon_x128.png",
58
    "type": "image/png"
59
  },
60
  {
61
    "purpose": "maskable",
62
    "sizes": "192x192",
63
    "src": "img/maskable_icon_x192.png",
64
    "type": "image/png"
65
  },
66
  {
67
    "purpose": "maskable",
68
    "sizes": "384x384",
69
    "src": "img/maskable_icon_x384.png",
70
    "type": "image/png"
71
  },
72
  {
73
    "purpose": "maskable",
74
    "sizes": "512x512",
75
    "src": "img/maskable_icon_x512.png",
76
    "type": "image/png"
77
  },
78
  {
79
    "purpose": "any",
80
    "sizes": "2730x2730",
81
    "src": "img/maskable_icon.png",
82
    "type": "image/png"
83
  },
84
  {
85
    "purpose": "any",
86
    "sizes": "48x48",
87
    "src": "img/maskable_icon_x48.png",
88
    "type": "image/png"
89
  },
90
  {
91
    "purpose": "any",
92
    "sizes": "72x72",
93
    "src": "img/maskable_icon_x72.png",
94
    "type": "image/png"
95
  },
96
  {
97
    "purpose": "any",
98
    "sizes": "96x96",
99
    "src": "img/maskable_icon_x96.png",
100
    "type": "image/png"
101
  },
102
  {
103
    "purpose": "any",
104
    "sizes": "128x128",
105
    "src": "img/maskable_icon_x128.png",
106
    "type": "image/png"
107
  },
108
  {
109
    "purpose": "any",
110
    "sizes": "192x192",
111
    "src": "img/maskable_icon_x192.png",
112
    "type": "image/png"
113
  },
114
  {
115
    "purpose": "any",
116
    "sizes": "384x384",
117
    "src": "img/maskable_icon_x384.png",
118
    "type": "image/png"
119
  },
120
  {
121
    "purpose": "any",
122
    "sizes": "512x512",
123
    "src": "img/maskable_icon_x512.png",
124
    "type": "image/png"
125
  },
126
  {
127
   "purpose": "any",
128
   "sizes": "2048x2048",
129
   "src": "img/icono2048.png",
130
   "type": "image/png"
131
  }
132
 ]
133
}

H. public / sw.js

1
/* Este archivo debe estar colocado en la carpeta raíz del sitio.
2
 * 
3
 * Cualquier cambio en el contenido de este archivo hace que el service
4
 * worker se reinstale. */
5
6
/**
7
 * Cambia el número de la versión cuando cambia el contenido de los
8
 * archivos.
9
 * 
10
 * El número a la izquierda del punto (.), en este caso <q>1</q>, se
11
 * conoce como número mayor y se cambia cuando se realizan
12
 * modificaciones grandes o importantes.
13
 * 
14
 * El número a la derecha del punto (.), en este caso <q>00</q>, se
15
 * conoce como número menor y se cambia cuando se realizan
16
 * modificaciones menores.
17
 */
18
const VERSION = "1.0"
19
20
/**
21
 * Nombre de la carpeta de caché.
22
 */
23
const CACHE = "pwamd"
24
25
/**
26
 * Archivos requeridos para que la aplicación funcione fuera de
27
 * línea.
28
 */
29
const ARCHIVOS = [
30
 "ayuda.html",
31
 "css/estilos.css",
32
 "css/material-symbols-outlined.css",
33
 "css/transicion_pestanas.css",
34
 "favicon.ico",
35
 "fonts/MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].codepoints",
36
 "fonts/MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].ttf",
37
 "fonts/MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].woff2",
38
 "fonts/Roboto-Italic-VariableFont_wdth,wght.ttf",
39
 "fonts/Roboto-VariableFont_wdth,wght.ttf",
40
 "img/icono2048.png",
41
 "img/maskable_icon.png",
42
 "img/maskable_icon_x128.png",
43
 "img/maskable_icon_x192.png",
44
 "img/maskable_icon_x384.png",
45
 "img/maskable_icon_x48.png",
46
 "img/maskable_icon_x512.png",
47
 "img/maskable_icon_x72.png",
48
 "img/maskable_icon_x96.png",
49
 "img/screenshot_horizontal.png",
50
 "img/screenshot_vertical.png",
51
 "index.html",
52
 "js/nav-tab-fixed.js",
53
 "js/registraServiceWorker.js",
54
 "libclienteweb/abreElementoHtml.js",
55
 "libclienteweb/cierraElementoHtmo.js",
56
 "libclienteweb/ES_APPLE.js",
57
 "libclienteweb/getAttribute.js",
58
 "libclienteweb/manejaErrores.js",
59
 "libclienteweb/muestraError.js",
60
 "libclienteweb/muestraTextoDeAyuda.js",
61
 "libclienteweb/ProblemDetailsError.js",
62
 "libclienteweb/querySelector.js",
63
 "libclienteweb/resaltaSiEstasEn.js",
64
 "libmde/md-app-bar.js",
65
 "libmde/md-filled-button.css",
66
 "libmde/md-filled-text-field.css",
67
 "libmde/md-list.css",
68
 "libmde/md-menu.css",
69
 "libmde/md-options-menu.js",
70
 "libmde/md-outline-button.css",
71
 "libmde/md-select-menu.js",
72
 "libmde/md-tab.css",
73
 "material-tokens/css/baseline.css",
74
 "material-tokens/css/colors.css",
75
 "material-tokens/css/elevation.css",
76
 "material-tokens/css/motion.css",
77
 "material-tokens/css/palette.css",
78
 "material-tokens/css/shape.css",
79
 "material-tokens/css/state.css",
80
 "material-tokens/css/theme/dark.css",
81
 "material-tokens/css/theme/light.css",
82
 "material-tokens/css/typography.css",
83
 "select.html",
84
 "site.webmanifest",
85
 "ungap/es.js",
86
 "/"
87
]
88
89
// Verifica si el código corre dentro de un service worker.
90
if (self instanceof ServiceWorkerGlobalScope) {
91
 // Evento al empezar a instalar el servide worker,
92
 self.addEventListener("install",
93
  (/** @type {ExtendableEvent} */ evt) => {
94
   console.log("El service worker se está instalando.")
95
   evt.waitUntil(llenaElCache())
96
  })
97
98
 // Evento al solicitar información a la red.
99
 self.addEventListener("fetch", (/** @type {FetchEvent} */ evt) => {
100
  if (evt.request.method === "GET") {
101
   evt.respondWith(buscaLaRespuestaEnElCache(evt))
102
  }
103
 })
104
105
 // Evento cuando el service worker se vuelve activo.
106
 self.addEventListener("activate",
107
  () => console.log("El service worker está activo."))
108
}
109
110
async function llenaElCache() {
111
 console.log("Intentando cargar caché:", CACHE)
112
 // Borra todos los cachés.
113
 const keys = await caches.keys()
114
 for (const key of keys) {
115
  await caches.delete(key)
116
 }
117
 // Abre el caché de este service worker.
118
 const cache = await caches.open(CACHE)
119
 // Carga el listado de ARCHIVOS.
120
 await cache.addAll(ARCHIVOS)
121
 console.log("Cache cargado:", CACHE)
122
 console.log("Versión:", VERSION)
123
}
124
125
/** @param {FetchEvent} evt */
126
async function buscaLaRespuestaEnElCache(evt) {
127
 // Abre el caché.
128
 const cache = await caches.open(CACHE)
129
 const request = evt.request
130
 /* Busca la respuesta a la solicitud en el contenido del caché, sin
131
  * tomar en cuenta la parte después del símbolo "?" en la URL. */
132
 const response = await cache.match(request, { ignoreSearch: true })
133
 if (response === undefined) {
134
  /* Si no la encuentra, empieza a descargar de la red y devuelve
135
   * la promesa. */
136
  return fetch(request)
137
 } else {
138
  // Si la encuentra, devuelve la respuesta encontrada en el caché.
139
  return response
140
 }
141
}

I. Carpeta « public / css »

1. public / css / estilos.css

1
html {
2
 /* Indica los temas del sistema operativo que son soportados. */
3
 color-scheme: light dark;
4
 --tabWidth: 3.75rem;
5
 --anchoNav: 22.5rem;
6
}
7
8
body>section,
9
form>section {
10
 max-width: 600px;
11
 margin-left: auto;
12
 margin-right: auto;
13
}
14
15
/* Fonts utilizados */
16
17
/* Definición de Roboto Variable (Normal) */
18
@font-face {
19
 font-family: 'Roboto';
20
 src: url('../fonts/Roboto-VariableFont_wdth,wght.ttf') format('truetype');
21
 /* Rango del eje de peso (wght) */
22
 font-weight: 100 900;
23
 /* Rango del eje de anchura (wdth) */
24
 font-stretch: 75% 100%;
25
 font-style: normal;
26
 /* Mejora el rendimiento de carga de texto */
27
 font-display: swap;
28
}
29
30
/* Definición de Roboto Variable (Itálica) */
31
@font-face {
32
 font-family: 'Roboto';
33
 src: url('../fonts/Roboto-Italic-VariableFont_wdth,wght.ttf') format('truetype');
34
 font-weight: 100 900;
35
 font-stretch: 75% 100%;
36
 font-style: italic;
37
 font-display: swap;
38
}
39
40
html {
41
 --Font: -apple-system, BlinkMacSystemFont, Roboto, sans-serif;
42
 --colIntIos: white;
43
 --colIntIosOnBk: #2acc2a;
44
 --colIntIosOnBkFc: #1bbb1b;
45
 --colIntIosOffBk: #dbdbdb;
46
 --colIntIosOffBkFc: #BDBDBD;
47
 /* Plain typeface */
48
 --md-ref-typeface-plain: var(--Font);
49
 /* Brand typeface */
50
 --md-ref-typeface-brand: var(--Font);
51
 --md-sys-typescale-label-large-weight-prominent:
52
  var(--md-ref-typeface-weight-bold);
53
 --md-box_shadow_level4:
54
  0 var(--md-sys-elevation-level4) var(--md-sys-elevation-level4) var(--md-sys-color-shadow);
55
 --md-box_shadow_level3:
56
  0 var(--md-sys-elevation-level3) var(--md-sys-elevation-level3) var(--md-sys-color-shadow);
57
 --md-box_shadow_level2:
58
  0 var(--md-sys-elevation-level2) var(--md-sys-elevation-level2) var(--md-sys-color-shadow);
59
 --md-box_shadow_level1:
60
  0 var(--md-sys-elevation-level1) var(--md-sys-elevation-level1) var(--md-sys-color-shadow);
61
 --md-box_shadow_level0: none;
62
 --iconSize: 1.5rem;
63
 --avatarSize: 2.5rem;
64
 --imageSize: 3.5rem;
65
 --videoWidth: 7.125rem;
66
 --videoHeight: 4rem;
67
 --md-sys-state-focus-indicator-outer-offset: 0.125rem;
68
 --md-sys-state-focus-indicator-thickness: 0.1875rem;
69
 /* Pressed state layer opacity */
70
 --state-pressed-transparency-percentage: 84%;
71
 /* Focus state layer opacity */
72
 --state-focus-transparency-percentage: 88%;
73
 /* Hover state layer opacity */
74
 --state-hover-transparency-percentage: 92%;
75
 background-color: var(--md-sys-color-background);
76
}
77
78
/* Quita un borde rojo que coloca Firefox. */
79
:-moz-ui-invalid {
80
 box-shadow: none;
81
}
82
83
body {
84
 margin: 0;
85
 font-family: var(--md-sys-typescale-body-large-font);
86
 font-weight: var(--md-sys-typescale-body-large-weight);
87
 font-size: var(--md-sys-typescale-body-large-size);
88
 font-style: var(--md-sys-typescale-body-large-font-style);
89
 letter-spacing: var(--md-sys-typescale-body-large-tracking);
90
 line-height: var(--md-sys-typescale-body-large-line-height);
91
 text-transform: var(--md-sys-typescale-body-large-text-transform);
92
 text-decoration: var(--md-sys-typescale-body-large-text-decoration);
93
 color: var(--md-sys-color-on-background);
94
 background-color: var(--md-sys-color-background);
95
}
96
97
p {
98
 margin: 1rem;
99
}
100
101
a {
102
 color: var(--md-sys-color-on-background);
103
}
104
105
@media (prefers-color-scheme: light) {
106
 html {
107
  --md-riple-color: #00000020;
108
 }
109
}
110
111
@media (prefers-color-scheme: dark) {
112
 html {
113
  --md-riple-color: #ffffff40;
114
 }
115
}
116
117
@keyframes md-ripple {
118
119
 from {
120
  background-size: 100%;
121
 }
122
123
 to {
124
  background-size: 15000%;
125
 }
126
127
}

2. public / css / material-symbols-outlined.css

1
@font-face {
2
 font-family: 'Material Symbols Outlined';
3
 font-style: normal;
4
 font-display: block;
5
 src:
6
  url(../fonts/MaterialSymbolsOutlined[FILL\,GRAD\,opsz\,wght].woff2) format('woff2'),
7
  url(../fonts/MaterialSymbolsOutlined[FILL\,GRAD\,opsz\,wght].ttf) format('truetype');
8
}
9
10
.material-symbols-outlined {
11
 font-family: 'Material Symbols Outlined';
12
 font-weight: normal;
13
 font-style: normal;
14
 font-display: block;
15
 font-size: 1.5rem;
16
 width: 1.5rem;
17
 height: 1.5rem;
18
 display: inline-block;
19
 line-height: 1;
20
 text-transform: none;
21
 letter-spacing: normal;
22
 word-wrap: normal;
23
 white-space: nowrap;
24
 direction: ltr;
25
}

3. public / css / transicion_pestanas.css

1
@view-transition {
2
 navigation: auto;
3
}
4
5
md-app-bar {
6
 view-transition-name: encabezado;
7
 background-color: var(--md-sys-color-surface);
8
 contain: layout;
9
}
10
11
section {
12
 view-transition-name: contenido;
13
 background-color: var(--md-sys-color-background);
14
 contain: layout;
15
}
16
17
@keyframes salePorLaIzquierda {
18
 from {
19
  transform: translateX(0);
20
  opacity: 1;
21
 }
22
23
 to {
24
  transform: translateX(-100%);
25
  opacity: 1;
26
 }
27
}
28
29
@keyframes entraPorLaDerecha {
30
 from {
31
  transform: translateX(100%);
32
  opacity: 1;
33
 }
34
35
 to {
36
  transform: translateX(0);
37
  opacity: 1;
38
 }
39
}
40
41
::view-transition-old(root),
42
::view-transition-new(root) {
43
 animation: none;
44
 mix-blend-mode: normal;
45
 opacity: 1 !important;
46
}
47
48
::view-transition-group(encabezado) {
49
 background-color: var(--md-sys-color-surface) !important;
50
}
51
52
::view-transition-group(contenido) {
53
 background-color: var(--md-sys-color-background) !important;
54
}
55
56
::view-transition-group(encabezado),
57
::view-transition-group(contenido) {
58
 animation-duration: var(--md-sys-motion-duration-1000);
59
 mix-blend-mode: normal !important;
60
 opacity: 1 !important;
61
 animation-fill-mode: both;
62
 overflow: hidden;
63
}
64
65
html::view-transition-old(encabezado) {
66
 animation-name: salePorLaIzquierda;
67
}
68
69
html::view-transition-new(encabezado) {
70
 animation-name: entraPorLaDerecha;
71
}
72
73
html::view-transition-old(contenido) {
74
 animation-name: salePorLaIzquierda;
75
}
76
77
html::view-transition-new(contenido) {
78
 animation-name: entraPorLaDerecha;
79
}

J. Carpeta « public / fonts »

1. public / fonts / MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].codepoints

2. public / fonts / MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].ttf

3. public / fonts / MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].woff2

4. public / fonts / Roboto-Italic-VariableFont_wdth,wght.ttf

5. public / fonts / Roboto-VariableFont_wdth,wght.ttf

K. Carpeta « img »

A. img / icono2048.png

icono2048.png

B. img / maskable_icon.png

maskable_icon.png

C. img / maskable_icon_x128.png

maskable_icon_x128.png

D. img / maskable_icon_x192.png

maskable_icon_x192.png

E. img / maskable_icon_x384.png

maskable_icon_x384.png

F. img / maskable_icon_x48.png

maskable_icon_x48.png

G. img / maskable_icon_x512.png

maskable_icon_x512.png

H. img / maskable_icon_x72.png

maskable_icon_x72.png

I. img / maskable_icon_x96.png

maskable_icon_x96.png

J. img / screenshot_horizontal.png

screenshot_horizontal.png

K. img / screenshot_vertical.png

screenshot_vertical.png

L. Carpeta « public / js »

1. public / js / nav-tab-fixed.js

1
import { resaltaSiEstasEn } from "../libclienteweb/resaltaSiEstasEn.js"
2
3
export class NavTabFixed extends HTMLElement {
4
5
 constructor() {
6
  super()
7
  this.creado = false
8
 }
9
10
 connectedCallback() {
11
  this.classList.add("md-tab", "fixed")
12
13
  if (!this.creado) {
14
15
   this.innerHTML = /* HTML */`
16
    <a ${resaltaSiEstasEn(["/index.html", "", "/"])} href="index.html">
17
     <span class="material-symbols-outlined">home</span>
18
     Inicio
19
    </a>
20
 
21
    <a ${resaltaSiEstasEn(["/select.html"])} href="select.html">
22
     <span class="material-symbols-outlined">bottom_panel_close</span>
23
     Select
24
    </a>
25
 
26
    <a ${resaltaSiEstasEn(["/ayuda.html"])} href="ayuda.html">
27
     <span class="material-symbols-outlined">help</span>
28
     Ayuda
29
    </a>`
30
31
   this.creado = true
32
33
  }
34
35
 }
36
37
}
38
39
customElements.define("nav-tab-fixed", NavTabFixed)

2. public / js / registraServiceWorker.js

1
const nombreDeServiceWorker = "sw.js"
2
3
try {
4
 navigator.serviceWorker.register(nombreDeServiceWorker)
5
  .then(registro => {
6
   console.log(nombreDeServiceWorker, "registrado.")
7
   console.log(registro)
8
  })
9
  .catch(error => console.log(error))
10
} catch (error) {
11
 console.log(error)
12
}

M. Carpeta « public / libclienteweb »

1. public / libclienteweb / abreElementoHtml.js

1
/**
2
 * @param { HTMLElement } elementoHtml
3
 */
4
export function abreElementoHtml(elementoHtml) {
5
 elementoHtml.classList.add("open")
6
}

2. public / libclienteweb / cierraElementoHtmo.js

1
/**
2
 * @param { HTMLElement } elementoHtml
3
 */
4
export function cierraElementoHtmo(elementoHtml) {
5
 elementoHtml.classList.remove("open")
6
}
7

3. public / libclienteweb / ES_APPLE.js

1
export const ES_APPLE = /.*(iPad|iPhone|iPod|Mac).*/.test(navigator.userAgent)

4. public / libclienteweb / getAttribute.js

1
/**
2
 * @param {HTMLElement} elementoHtml
3
 * @param {string} nombre
4
 * @returns {string}
5
 */
6
export function getAttribute(elementoHtml, nombre) {
7
 const valor = elementoHtml.getAttribute(nombre)
8
 return valor === null ? "" : valor
9
}

5. public / libclienteweb / manejaErrores.js

1
import { muestraError } from "./muestraError.js"
2
3
/**
4
 * Intercepta Response.prototype.json para capturar errores de parseo
5
 * y asegurar que se reporten correctamente en navegadores Chromium.
6
 */
7
{
8
 const originalJson = Response.prototype.json
9
10
 Response.prototype.json = function () {
11
  // Llamamos al método original usando el contexto (this) de la respuesta
12
  return originalJson.call(this)
13
   .catch((/** @type {any} */ error) => {
14
    // Corrige un error de Chrome que evita el manejo correcto de errores.
15
    throw new Error(error)
16
   })
17
 }
18
}
19
20
window.onerror = function (
21
 /** @type {Event | string} */ _event,
22
 /** @type {string | undefined} */ _fuente,
23
 /** @type {number | undefined} */ _numeroDeLinea,
24
 /** @type {number | undefined} */ _numeroDeColumna,
25
 /** @type {Error | undefined} */ error
26
) {
27
 muestraError(error)
28
 return true
29
}
30
31
window.addEventListener('unhandledrejection', event => {
32
 muestraError(event.reason)
33
 event.preventDefault()
34
})

6. public / libclienteweb / muestraError.js

1
import { ProblemDetailsError } from "./ProblemDetailsError.js"
2
3
/**
4
 * Muestra los datos de una Error en la consola y en un cuadro de alerta.
5
 * @param { unknown } error descripción del error.
6
 */
7
export function muestraError(error) {
8
9
 if (error instanceof ProblemDetailsError) {
10
11
  const problemDetails = error.problemDetails
12
13
  let mensaje =
14
   typeof problemDetails["title"] === "string" ? problemDetails["title"] : ""
15
  if (typeof problemDetails["detail"] === "string") {
16
   if (mensaje !== "") {
17
    mensaje += "\n"
18
   }
19
   mensaje += problemDetails["detail"]
20
  }
21
  if (mensaje === "") {
22
   mensaje = "Error"
23
  }
24
  console.error(error, problemDetails)
25
  alert(mensaje)
26
27
 } else if (
28
  typeof error === "object" && error !== null && "message" in error
29
 ) {
30
31
  console.error(error)
32
  alert(error.message)
33
34
 } else {
35
36
  console.error("Error", error)
37
  alert("Error")
38
39
 }
40
41
}

7. public / libclienteweb / muestraTextoDeAyuda.js

1
/**
2
 * Si un elemento HTML tiene un mensaje de validación, lo
3
 * muestra en su elemento de ayuda; en caso contrario, muestra
4
 * un mensaje de ayuda. 
5
 * @param { {
6
 *   validity: { valid: boolean };
7
 *   validationMessage: string
8
 *  } } elementoHtml elemento que contiene datos de validación.
9
 * @param { HTMLElement } elementoDeAyuda elemento fonde
10
 * se muestran los elementos de validación para elementoHtml.
11
 * @param { string } mensajeDeAyuda mensaje de ayuda cuando el
12
 *  estado de elementoHtml es válido.
13
 */
14
export function muestraTextoDeAyuda(elementoHtml, elementoDeAyuda,
15
 mensajeDeAyuda) {
16
 if (elementoHtml.validity.valid) {
17
  elementoDeAyuda.textContent = mensajeDeAyuda
18
 } else {
19
  elementoDeAyuda.textContent = elementoHtml.validationMessage
20
 }
21
}

8. public / libclienteweb / ProblemDetailsError.js

1
export class ProblemDetailsError extends Error {
2
3
 /**
4
  * Detalle de los errores devueltos por un servicio.
5
  * Crea una instancia de ProblemDetailsError.
6
  * @param {any} problemDetails Objeto con la descripcipon del error.
7
  */
8
 constructor(problemDetails) {
9
10
  super(
11
   typeof problemDetails["detail"] === "string"
12
    ? problemDetails["detail"]
13
    : (
14
     typeof problemDetails["title"] === "string"
15
      ? problemDetails["title"]
16
      : "Error"
17
    )
18
  )
19
20
  this.problemDetails = problemDetails
21
22
 }
23
24
}

9. public / libclienteweb / querySelector.js

1
/**
2
 * @template { HTMLElement } T
3
 * @param { Document | Element | ShadowRoot } raiz
4
 * @param { string } query
5
 * @returns { T }
6
 */
7
export function querySelector(raiz, query) {
8
 /** @type { T | null } */
9
 const resutado = raiz.querySelector(query)
10
 if (resutado === null)
11
  throw new Error(`No se encuentra ${query}.`)
12
 return resutado
13
}

10. public / libclienteweb / resaltaSiEstasEn.js

1
/**
2
 * @param {string[]} paginas
3
 */
4
export function resaltaSiEstasEn(paginas) {
5
6
 const pathname = location.pathname
7
8
 for (const pagina of paginas) {
9
10
  if (pathname === pagina) {
11
   queueMicrotask(() => {
12
    const tab = document.querySelector(".active")
13
    if (tab !== null && tab.closest(".scrollable") !== null) {
14
     tab.scrollIntoView({ inline: "center", block: "end" })
15
    }
16
   })
17
   return `class="active"`
18
  }
19
20
 }
21
22
 return ""
23
24
}

N. Carpeta « public / libmde »

1. public / libmde / md-app-bar.js

1
import { ES_APPLE } from "../libclienteweb/ES_APPLE.js"
2
import { getAttribute } from "../libclienteweb/getAttribute.js"
3
import { querySelector } from "../libclienteweb/querySelector.js"
4
5
class MdAppBar extends HTMLElement {
6
7
 getContent() {
8
  return /* HTML */`
9
   <style>
10
11
    :host {
12
     display: flex;
13
     box-sizing: border-box;
14
     align-items: center;
15
     padding: 0 0.25rem;
16
     background-color: var(--md-sys-color-surface);
17
     position: sticky;
18
     z-index: 1;
19
     left: env(titlebar-area-x, 0);
20
     top: env(titlebar-area-y, 0);
21
     height: env(titlebar-area-height, 4rem);
22
     width: env(titlebar-area-width, 100%);
23
    }
24
    
25
    :host(.apple) {
26
     height: env(titlebar-area-height, 3rem);
27
    }
28
29
    :host(.scroll) {
30
     background-color: var(--md-sys-color-surface-container-low);
31
    }
32
33
    #navigation {
34
     flex: 0 0 auto;
35
     overflow: hidden
36
    }
37
38
    #navigation ::slotted(*) {
39
     color: var(--md-sys-color-on-surface);
40
    }
41
42
    #acciones {
43
     margin-left: auto;
44
     flex: 0 0 auto;
45
     overflow: hidden
46
    }
47
48
    :host(.centered) #navigation,
49
    :host(.centered) #acciones {
50
     flex: 0 0 6rem;
51
     overflow: hidden
52
    }
53
54
    #headline::slotted(*) {
55
     -webkit-app-region: drag;
56
     flex: 1 1 auto;
57
     white-space: nowrap;
58
     text-overflow: ellipsis;
59
     overflow: hidden;
60
     font-family: var(--md-sys-typescale-title-large-font);
61
     font-weight: var(--md-sys-typescale-title-large-weight);
62
     font-size: var(--md-sys-typescale-title-large-size);
63
     font-style: var(--md-sys-typescale-title-large-font-style);
64
     letter-spacing: var(--md-sys-typescale-title-large-tracking);
65
     line-height: var(--md-sys-typescale-title-large-line-height);
66
     text-transform: var(--md-sys-typescale-title-large-text-transform);
67
     text-decoration: var(--md-sys-typescale-title-large-text-decoration);
68
     color: var(--md-sys-color-on-surface);
69
    }
70
71
    :host(.centered) #headline::slotted(*) {
72
     flex: 1 1 auto;
73
     text-align: center
74
    }
75
76
   </style>
77
78
   <span id="navigation">
79
    <slot name="navigation"></slot>
80
   </span>
81
   <slot id="headline"></slot>
82
   <span id="acciones">
83
    <slot name="action"></slot>
84
   </span>`
85
 }
86
87
 constructor() {
88
  super()
89
  if (ES_APPLE) {
90
   document.body.classList.add("apple")
91
   document.body.classList.remove("material")
92
  } else {
93
   document.body.classList.add("material")
94
   document.body.classList.remove("apple")
95
  }
96
97
  /**
98
   * @private
99
   * @readonly
100
   */
101
  const shadow = this.attachShadow({ mode: "open" })
102
  shadow.innerHTML = this.getContent()
103
  this._configuraAction = this._configuraAction.bind(this)
104
  /**
105
   * @private
106
   * @type {number}
107
   */
108
  this._posY = 0
109
  /**
110
   * @private
111
   * @type {boolean}
112
   */
113
  this._scrolling = false
114
  /**
115
    * @private
116
    * @type { HTMLSlotElement }
117
    */
118
  this._navigation = querySelector(shadow, '[name="navigation"]')
119
  /**
120
    * @private
121
    * @type { HTMLSlotElement }
122
    */
123
  this._action = querySelector(shadow, '[name="action"]')
124
  /**
125
    * @private
126
    * @type { HTMLHeadingElement | null }
127
    */
128
  this._headline = null
129
  /**
130
    * @private
131
    * @type { HTMLElement | null }
132
    */
133
  this._adicional = null
134
  this._action.addEventListener("slotchange", this._configuraAction)
135
  addEventListener("scroll", () => this._onScroll())
136
  addEventListener("load", () => this.configurOtros())
137
 }
138
139
 connectedCallback() {
140
  this.role = "toolbar"
141
  this._configuraAction()
142
 }
143
144
 configurOtros() {
145
  const idAdicional = getAttribute(this, "adicional")
146
  if (idAdicional !== "") {
147
   this._adicional = document.getElementById(idAdicional)
148
   if (this._adicional !== null) {
149
    if (this.classList.contains("apple")) {
150
     this._adicional.style.top = "env(titlebar-area-height, 3rem)"
151
    } else {
152
     this._adicional.style.top = "env(titlebar-area-height, 4rem)"
153
    }
154
   }
155
  }
156
 }
157
158
 _configuraAction() {
159
  const assignedElements = this._action.assignedElements()
160
  if (this.isConnected) {
161
   if (ES_APPLE) {
162
    this.classList.add("apple")
163
    this.classList.remove("material")
164
   } else {
165
    this.classList.add("material")
166
    this.classList.remove("apple")
167
   }
168
   if (this.classList.contains("centered")) {
169
    this.classList.remove("centrado")
170
    this.classList.remove("justificado")
171
   } else {
172
    if (ES_APPLE && assignedElements.length <= 1) {
173
     this.classList.add("centrado")
174
     this.classList.remove("justificado")
175
    } else {
176
     this.classList.add("justificado")
177
     this.classList.remove("centrado")
178
    }
179
   }
180
  }
181
 }
182
183
 /** @private */
184
 _onScroll() {
185
  this._posY = scrollY
186
  if (!this._scrolling) {
187
   requestAnimationFrame(() => this._avanza())
188
  }
189
  this._scrolling = true
190
 }
191
192
 /** @private */
193
 _avanza() {
194
  if (this._posY === 0) {
195
   this.classList.remove("scroll")
196
   if (this._headline !== null) {
197
    if (this._adicional === null) {
198
     this._headline.classList.remove("scroll")
199
    } else {
200
     this._headline.classList.remove("scroll-adicional")
201
    }
202
   }
203
   if (this._adicional !== null) {
204
    this._adicional.classList.remove("scroll")
205
   }
206
  } else {
207
   this.classList.add("scroll")
208
   if (this._headline !== null) {
209
    if (this._adicional === null) {
210
     this._headline.classList.add("scroll")
211
    } else {
212
     this._headline.classList.add("scroll-adicional")
213
    }
214
   }
215
   if (this._adicional !== null) {
216
    this._adicional.classList.add("scroll")
217
   }
218
  }
219
  this._scrolling = false
220
 }
221
222
}
223
224
customElements.define("md-app-bar", MdAppBar)

2. public / libmde / md-filled-button.css

1
/* container */
2
.md-filled-button::before {
3
 content: "";
4
 position: absolute;
5
 z-index: -2;
6
 top: 0;
7
 right: 0;
8
 left: 0;
9
 bottom: 0;
10
 background-color: var(--md-sys-color-primary);
11
}
12
13
/* state layer */
14
.md-filled-button::after {
15
 content: "";
16
 position: absolute;
17
 z-index: -1;
18
 top: 0;
19
 right: 0;
20
 left: 0;
21
 bottom: 0;
22
 background-color: transparent;
23
}
24
25
/* label, shape */
26
.md-filled-button {
27
 position: relative;
28
 box-sizing: border-box;
29
 border-radius: 1.25rem;
30
 height: 2.5rem;
31
 line-height: 2.5rem;
32
 padding: 0 1.5rem;
33
 border: none;
34
 background-color: transparent;
35
 box-shadow: var(--md-box_shadow_level0);
36
 font-family: var(--md-sys-typescale-label-large-font);
37
 font-weight: var(--md-sys-typescale-label-large-weight);
38
 font-size: var(--md-sys-typescale-label-large-size);
39
 font-style: var(--md-sys-typescale-label-large-font-style);
40
 letter-spacing: var(--md-sys-typescale-label-large-tracking);
41
 text-transform: var(--md-sys-typescale-label-large-text-transform);
42
 text-decoration: var(--md-sys-typescale-label-large-text-decoration);
43
 color: var(--md-sys-color-on-primary);
44
 white-space: nowrap;
45
 text-overflow: ellipsis;
46
 overflow: hidden;
47
}
48
49
/* label, shape */
50
.md-filled-button:hover {
51
 color: var(--md-sys-color-on-primary);
52
 box-shadow: var(--md-box_shadow_level1);
53
}
54
55
/* state layer */
56
.md-filled-button:hover::after {
57
 background-color: var(--md-sys-color-on-primary);
58
 opacity: var(--md-sys-state-hover-state-layer-opacity);
59
}
60
61
/* label, shape */
62
.md-filled-button:focus {
63
 outline: none;
64
 color: var(--md-sys-color-on-primary);
65
 box-shadow: var(--md-box_shadow_level0) !important;
66
}
67
68
/* state layer */
69
.md-filled-button:focus::after {
70
 background-color: var(--md-sys-color-on-primary);
71
 opacity: var(--md-sys-state-focus-state-layer-opacity);
72
}
73
74
/* label, shape */
75
.md-filled-button:active {
76
 color: var(--md-sys-color-on-primary);
77
 background-position: center;
78
 background-image:
79
  radial-gradient(circle, var(--md-sys-color-on-primary-container) 1%, transparent 1%);
80
 background-size: 100%;
81
 animation-name: md-ripple;
82
 animation-duration: var(--md-sys-motion-duration-500);
83
 box-shadow: var(--md-box_shadow_level0) !important;
84
}
85
86
/* state layer */
87
.md-filled-button:active::after {
88
 background-color: var(--md-sys-color-on-primary);
89
 opacity: var(--md-sys-state-pressed-state-layer-opacity);
90
}
91
92
/* label, shape */
93
.md-filled-button:disabled {
94
 background-color: transparent !important;
95
 color: var(--md-sys-color-on-surface) !important;
96
 opacity: 0.38 !important;
97
 box-shadow: var(--md-box_shadow_level0) !important;
98
}
99
100
/* container */
101
.md-filled-button:disabled::before {
102
 background-color: var(--md-sys-color-on-surface) !important;
103
 opacity: 0.12 !important;
104
}
105
106
/* state layer */
107
.md-filled-button:disabled::after {
108
 background-color: transparent !important;
109
 opacity: 1 !important;
110
}

3. public / libmde / md-filled-text-field.css

1
.md-filled-text-field {
2
 position: relative;
3
 overflow: hidden;
4
 display: flex;
5
 flex-direction: column;
6
 align-items: stretch;
7
 padding-top: calc(0.5rem + var(--md-sys-typescale-body-small-line-height));
8
 border-top-left-radius: var(--md-sys-shape-corner-extra-small-top-top-left);
9
 border-top-right-radius: var(--md-sys-shape-corner-extra-small-top-top-right);
10
 overflow: hidden;
11
}
12
13
/* container */
14
.md-filled-text-field::before {
15
 content: "";
16
 position: absolute;
17
 z-index: -2;
18
 top: 0;
19
 right: 0;
20
 left: 0;
21
 bottom: 0;
22
 background-color: var(--md-sys-color-surface-container-highest);
23
}
24
25
/* state layer */
26
.md-filled-text-field::after {
27
 content: "";
28
 position: absolute;
29
 z-index: -1;
30
 top: 0;
31
 right: 0;
32
 left: 0;
33
 bottom: 0;
34
 background-color: transparent;
35
}
36
37
.md-filled-text-field span,
38
.md-filled-text-field label {
39
 position: absolute;
40
 top: 0.5rem;
41
 left: 1rem;
42
 right: 1rem;
43
 display: block;
44
 transform: translateY(1rem);
45
 transition-property: transform;
46
 transition-duration: var(--md-sys-motion-duration-300);
47
 white-space: nowrap;
48
 text-overflow: ellipsis;
49
 overflow: hidden;
50
 color: var(--md-sys-color-on-surface-variant);
51
 font-family: var(--md-sys-typescale-body-large-font);
52
 font-weight: var(--md-sys-typescale-body-large-weight);
53
 font-size: var(--md-sys-typescale-body-large-size);
54
 font-style: var(--md-sys-typescale-body-large-font-style);
55
 letter-spacing: var(--md-sys-typescale-body-large-tracking);
56
 line-height: var(--md-sys-typescale-body-large-line-height);
57
 text-transform: var(--md-sys-typescale-body-large-text-transform);
58
 text-decoration: var(--md-sys-typescale-body-large-text-decoration);
59
}
60
61
.md-filled-text-field input:not(:placeholder-shown)+span,
62
.md-filled-text-field input:not(:placeholder-shown)+label,
63
.md-filled-text-field textarea:not(:placeholder-shown)+span,
64
.md-filled-text-field textarea:not(:placeholder-shown)+label,
65
.md-filled-text-field .populated+span,
66
.md-filled-text-field .populated+label,
67
.md-filled-text-field:focus-within span,
68
.md-filled-text-field:focus-within label,
69
.md-filled-text-field.float span,
70
.md-filled-text-field.float label {
71
 transform: translateY(0);
72
 font-family: var(--md-sys-typescale-body-small-font);
73
 font-weight: var(--md-sys-typescale-body-small-weight);
74
 font-size: var(--md-sys-typescale-body-small-size);
75
 font-style: var(--md-sys-typescale-body-small-font-style);
76
 letter-spacing: var(--md-sys-typescale-body-small-tracking);
77
 line-height: var(--md-sys-typescale-body-small-line-height);
78
 text-transform: var(--md-sys-typescale-body-small-text-transform);
79
 text-decoration: var(--md-sys-typescale-body-small-text-decoration);
80
}
81
82
.md-filled-text-field :not(label, span, small) {
83
 position: relative;
84
 caret-color: var(--md-sys-color-primary);
85
 min-height: 2rem;
86
 box-sizing: border-box;
87
 padding-left: 1rem;
88
 padding-bottom: 0.5rem;
89
 padding-right: 1rem;
90
 border: none;
91
 resize: none;
92
 color: var(--md-sys-color-on-surface);
93
 font-family: var(--md-sys-typescale-body-large-font);
94
 font-weight: var(--md-sys-typescale-body-large-weight);
95
 font-size: var(--md-sys-typescale-body-large-size);
96
 font-style: var(--md-sys-typescale-body-large-font-style);
97
 letter-spacing: var(--md-sys-typescale-body-large-tracking);
98
 line-height: var(--md-sys-typescale-body-large-line-height);
99
 text-transform: var(--md-sys-typescale-body-large-text-transform);
100
 text-decoration: var(--md-sys-typescale-body-large-text-decoration);
101
 background-color: transparent;
102
 outline: none;
103
 border-bottom-width: 0.0625rem;
104
 border-bottom-style: solid;
105
 border-bottom-color: var(--md-sys-color-on-surface-variant);
106
}
107
108
.md-filled-text-field ::placeholder {
109
 color: transparent;
110
}
111
112
.md-filled-text-field small {
113
 display: block;
114
 color: var(--md-sys-color-on-surface-variant);
115
 background-color: var(--md-sys-color-background);
116
 font-family: var(--md-sys-typescale-body-small-font);
117
 font-weight: var(--md-sys-typescale-body-small-weight);
118
 font-size: var(--md-sys-typescale-body-small-size);
119
 font-style: var(--md-sys-typescale-body-small-font-style);
120
 letter-spacing: var(--md-sys-typescale-body-small-tracking);
121
 line-height: var(--md-sys-typescale-body-small-line-height);
122
 text-transform: var(--md-sys-typescale-body-small-text-transform);
123
 text-decoration: var(--md-sys-typescale-body-small-text-decoration);
124
 padding: 0.25rem 1rem 0 1rem;
125
 white-space: nowrap;
126
 text-overflow: ellipsis;
127
 overflow: hidden;
128
}
129
130
.md-filled-text-field:hover span,
131
.md-filled-text-field:hover label {
132
 color: var(--md-sys-color-on-surface-variant);
133
}
134
135
.md-filled-text-field:hover :not(label, span, small) {
136
 padding-bottom: 0.5rem;
137
 border-bottom-width: 0.0625rem;
138
 border-bottom-color: var(--md-sys-color-on-surface);
139
}
140
141
.md-filled-text-field:hover::after {
142
 background-color: var(--md-sys-color-on-surface);
143
 opacity: var(--md-sys-state-hover-state-layer-opacity);
144
}
145
146
.md-filled-text-field:hover small {
147
 color: var(--md-sys-color-on-surface-variant);
148
}
149
150
.md-filled-text-field:focus-within span,
151
.md-filled-text-field:focus-within label {
152
 color: var(--md-sys-color-primary);
153
}
154
155
.md-filled-text-field :focus {
156
 color: var(--md-sys-color-on-surface);
157
 outline: none;
158
 padding-bottom: 0.4375rem;
159
 border-bottom-width: 0.125rem;
160
 border-bottom-color: var(--md-sys-color-primary);
161
}
162
163
.md-filled-text-field:hover :focus {
164
 padding-bottom: 0.4375rem;
165
 border-bottom-width: 0.125rem;
166
}
167
168
.md-filled-text-field:focus-within small {
169
 color: var(--md-sys-color-on-surface-variant);
170
}
171
172
.md-filled-text-field :invalid {
173
 color: var(--md-sys-color-on-surface);
174
 border-bottom-color: var(--md-sys-color-error);
175
}
176
177
.md-filled-text-field :invalid+span,
178
.md-filled-text-field :invalid+label {
179
 color: var(--md-sys-color-error);
180
}
181
182
.md-filled-text-field :invalid~small,
183
.md-filled-text-field:hover :invalid~small,
184
.md-filled-text-field:focus-within .input-text:invalid~small {
185
 color: var(--md-sys-color-error);
186
}
187
188
.md-filled-text-field :invalid:focus {
189
 caret-color: var(--md-sys-color-error);
190
 border-bottom-color: var(--md-sys-color-error);
191
}
192
193
.md-filled-text-field:hover :invalid {
194
 color: var(--md-sys-color-on-surface);
195
 border-bottom-color: var(--md-sys-color-error);
196
}

4. public / libmde / md-list.css

1
.md-list {
2
 margin: 0.5rem 0;
3
 padding: 0;
4
 list-style-type: none;
5
}
6
7
.md-list .md-one-line,
8
.md-list .md-two-line,
9
.md-list .md-three-line {
10
 position: relative;
11
 display: flex;
12
 box-sizing: border-box;
13
}
14
15
/* container */
16
.md-list .md-one-line::before,
17
.md-list .md-two-line::before,
18
.md-list .md-three-line::before {
19
 content: "";
20
 position: absolute;
21
 z-index: -2;
22
 top: 0;
23
 right: 0;
24
 left: 0;
25
 bottom: 0;
26
 background-color: var(--md-sys-color-surface);
27
}
28
29
/* state layer */
30
.md-list .md-one-line::after,
31
.md-list .md-two-line::after,
32
.md-list .md-three-line::after {
33
 content: "";
34
 position: absolute;
35
 z-index: -1;
36
 top: 0;
37
 right: 0;
38
 left: 0;
39
 bottom: 0;
40
 background-color: transparent;
41
}
42
43
.md-list .md-one-line {
44
 align-items: center;
45
 gap: 1rem;
46
 min-height: 3.5rem;
47
 padding: 0.5rem 1.5rem 0.5rem 1rem;
48
}
49
50
.md-list .md-one-line.video,
51
.md-list .md-two-line.video {
52
 padding: 0.75rem 1.5rem 0.75rem 0;
53
}
54
55
.md-list .md-two-line,
56
.md-list .md-three-line {
57
 flex-flow: column;
58
}
59
60
.md-list .md-two-line {
61
 justify-content: center;
62
 min-height: 4.5rem;
63
 padding: 0.5rem 1.5rem 0.5rem 1rem;
64
}
65
66
.md-list .md-two-line.icon,
67
.md-list .md-two-line.avatar,
68
.md-list .md-two-line.image,
69
.md-list .md-two-line.video,
70
.md-list .md-three-line.icon,
71
.md-list .md-three-line.avatar,
72
.md-list .md-three-line.image,
73
.md-list .md-three-line.video {
74
 display: grid;
75
 column-gap: 1rem;
76
 row-gap: 0;
77
 grid-template-areas:
78
  "img headline"
79
  "img supporting";
80
}
81
82
.md-list .md-two-line.icon,
83
.md-list .md-two-line.avatar,
84
.md-list .md-two-line.image,
85
.md-list .md-two-line.video {
86
 align-content: center;
87
 grid-template-rows: 1fr 1fr;
88
}
89
90
.md-list .md-two-line.icon,
91
.md-list .md-three-line.icon {
92
 grid-template-columns: var(--iconSize) 1fr;
93
}
94
95
.md-list .md-two-line.avatar,
96
.md-list .md-three-line.avatar {
97
 grid-template-columns: var(--avatarSize) 1fr;
98
}
99
100
.md-list .md-two-line.image,
101
.md-list .md-three-line.image {
102
 grid-template-columns: var(--imageSize) 1fr;
103
}
104
105
.md-list .md-two-line.video,
106
.md-list .md-three-line.video {
107
 grid-template-columns: var(--videoWidth) 1fr;
108
}
109
110
.md-list .md-three-line {
111
 align-content: flex-start;
112
 min-height: 5.5rem;
113
 padding: 0.75rem 1.5rem 0.75rem 1rem;
114
}
115
116
.md-list .md-three-line.video {
117
 padding: 0.75rem 1.5rem 0.75rem 0;
118
}
119
120
.md-list .md-three-line.icon,
121
.md-list .md-three-line.avatar,
122
.md-list .md-three-line.image,
123
.md-list .md-three-line.video {
124
 align-content: start;
125
 grid-template-rows: var(--md-sys-typescale-label-large-line-height) 1fr;
126
}
127
128
/* state layer */
129
.md-list .md-one-line:hover::after,
130
.md-list .md-two-line:hover::after,
131
.md-list .md-three-line:hover::after {
132
 background-color: var(--md-sys-color-on-surface);
133
 opacity: var(--md-sys-state-hover-state-layer-opacity);
134
}
135
136
/* state layer */
137
.md-list a.md-one-line:focus::after,
138
.md-list a.md-two-line:focus::after,
139
.md-list a.md-three-line:focus::after,
140
.md-list a.md-one-line:focus-visible::after,
141
.md-list a.md-two-line:focus-visible::after,
142
.md-list a.md-three-line:focus-visible::after {
143
 background-color: var(--md-sys-color-on-surface);
144
 opacity: var(--md-sys-state-focus-state-layer-opacity);
145
}
146
147
.md-list a:focus,
148
.md-list a:focus-visible {
149
 outline: none;
150
}
151
152
.md-list a:active {
153
 background-position: center;
154
 background-image:
155
  radial-gradient(circle, var(--md-riple-color) 1%, transparent 1%);
156
 background-size: 100%;
157
 animation-name: md-ripple;
158
 animation-duration: var(--md-sys-motion-duration-500);
159
 box-shadow: var(--md-box_shadow_level0) !important;
160
}
161
162
/* state layer */
163
.md-list a.md-one-line:active::after,
164
.md-list a.md-two-line:active::after,
165
.md-list a.md-three-line:active::after {
166
 background-color: var(--md-sys-color-on-surface);
167
 opacity: var(--md-sys-state-pressed-state-layer-opacity);
168
}
169
170
.md-list a.md-two-line,
171
.md-list a.md-three-line {
172
 text-decoration: none;
173
}
174
175
.md-list a.md-two-line .headline,
176
.md-list a.md-three-line .headline {
177
 text-decoration: underline;
178
}
179
180
.md-list .headline {
181
 grid-area: headline;
182
 display: block;
183
 box-sizing: border-box;
184
 color: var(--md-sys-color-on-surface);
185
 font-family: var(--md-sys-typescale-body-large-font);
186
 font-weight: var(--md-sys-typescale-body-large-weight);
187
 font-size: var(--md-sys-typescale-body-large-size);
188
 font-style: var(--md-sys-typescale-body-large-font-style);
189
 letter-spacing: var(--md-sys-typescale-body-large-tracking);
190
 line-height: var(--md-sys-typescale-body-large-line-height);
191
 text-transform: var(--md-sys-typescale-body-large-text-transform);
192
 text-decoration: var(--md-sys-typescale-body-large-text-decoration);
193
 max-height: var(--md-sys-typescale-body-large-line-height);
194
 white-space: nowrap;
195
 text-overflow: ellipsis;
196
 overflow: hidden;
197
}
198
199
.md-list .md-two-line.icon .headline,
200
.md-list .md-two-line.avatar .headline,
201
.md-list .md-two-line.image .headline,
202
.md-list .md-two-line.video .headline,
203
.md-list .md-three-line.icon .headline,
204
.md-list .md-three-line.avatar .headline,
205
.md-list .md-three-line.image .headline,
206
.md-list .md-three-line.video .headline {
207
 align-self: end;
208
}
209
210
.md-list .supporting {
211
 grid-area: supporting;
212
 display: -webkit-box;
213
 -webkit-box-orient: vertical;
214
 overflow: hidden;
215
 box-sizing: border-box;
216
 align-self: start;
217
 font-family: var(--md-sys-typescale-body-medium-font);
218
 font-weight: var(--md-sys-typescale-body-medium-weight);
219
 font-size: var(--md-sys-typescale-body-medium-size);
220
 font-style: var(--md-sys-typescale-body-medium-font-style);
221
 letter-spacing: var(--md-sys-typescale-body-medium-tracking);
222
 line-height: var(--md-sys-typescale-body-medium-line-height);
223
 text-transform: var(--md-sys-typescale-body-medium-text-transform);
224
 text-decoration: var(--md-sys-typescale-body-medium-text-decoration);
225
}
226
227
.md-list .md-two-line .supporting {
228
 max-height: var(--md-sys-typescale-body-medium-line-height);
229
 line-clamp: 1;
230
 -webkit-line-clamp: 1;
231
}
232
233
.md-list .md-three-line .supporting {
234
 max-height: calc(2 * var(--md-sys-typescale-body-medium-line-height));
235
 line-clamp: 2;
236
 -webkit-line-clamp: 2;
237
}
238
239
.md-list .avatar img,
240
.md-list .avatar label,
241
.md-list .avatar .material-symbols-outlined:first-child {
242
 flex-shrink: 0;
243
 background-color: var(--md-sys-color-primary-container);
244
 color: var(--md-sys-color-on-primary-container);
245
 border-radius: 50%;
246
 width: var(--avatarSize);
247
 height: var(--avatarSize);
248
}
249
250
.md-list .avatar label {
251
 display: inline-block;
252
 font-family: var(--md-sys-typescale-title-medium-font);
253
 font-weight: var(--md-sys-typescale-title-medium-weight);
254
 font-size: var(--md-sys-typescale-title-medium-size);
255
 font-style: var(--md-sys-typescale-title-medium-font-style);
256
 letter-spacing: var(--md-sys-typescale-title-medium-tracking);
257
 line-height: var(--md-sys-typescale-title-medium-line-height);
258
 text-transform: var(--md-sys-typescale-title-medium-text-transform);
259
 text-decoration: var(--md-sys-typescale-title-medium-text-decoration);
260
 overflow: hidden;
261
}
262
263
.md-list .avatar .material-symbols-outlined:first-child {
264
 font-size: var(--avatarSize);
265
}
266
267
.md-list .avatar.md-two-line img,
268
.md-list .avatar.md-two-line label,
269
.md-list .avatar.md-two-line .material-symbols-outlined:first-child {
270
 grid-area: img;
271
 align-self: center;
272
}
273
274
.md-list .avatar.md-three-line img,
275
.md-list .avatar.md-three-line label,
276
.md-list .avatar.md-three-line .material-symbols-outlined:first-child {
277
 grid-area: img;
278
 align-self: start;
279
}
280
281
.md-list .icon img,
282
.md-list .icon .material-symbols-outlined:first-child {
283
 flex-shrink: 0;
284
 color: var(--md-sys-color-on-surface-variant);
285
 width: var(--iconSize);
286
 height: var(--iconSize);
287
}
288
289
.md-list .icon .material-symbols-outlined:first-child {
290
 font-size: var(--iconSize);
291
}
292
293
.md-list .icon.md-two-line img,
294
.md-list .icon.md-two-line .material-symbols-outlined:first-child {
295
 grid-area: img;
296
 align-self: center;
297
}
298
299
.md-list .icon.md-three-line img,
300
.md-list .icon.md-three-line .material-symbols-outlined:first-child {
301
 grid-area: img;
302
 align-self: start;
303
}
304
305
.md-list .video img {
306
 flex-shrink: 0;
307
 color: var(--md-sys-color-on-surface-variant);
308
 width: var(--videoWidth);
309
 height: var(--videoHeight);
310
}
311
312
.md-list .video.md-two-line img {
313
 grid-area: img;
314
 align-self: center;
315
}
316
317
.md-list .video.md-three-line img {
318
 grid-area: img;
319
 align-self: start;
320
}
321
322
.md-list .image img,
323
.md-list .image .material-symbols-outlined:first-child {
324
 flex-shrink: 0;
325
 color: var(--md-sys-color-on-surface-variant);
326
 width: var(--imageSize);
327
 height: var(--imageSize);
328
}
329
330
.md-list .image .material-symbols-outlined:first-child {
331
 font-size: var(--imageSize);
332
}
333
334
.md-list .image.md-two-line img,
335
.md-list .image.md-two-line .material-symbols-outlined:first-child {
336
 grid-area: img;
337
 align-self: center;
338
}
339
340
.md-list .image.md-three-line img,
341
.md-list .image.md-three-line .material-symbols-outlined:first-child {
342
 grid-area: img;
343
 align-self: start;
344
}

5. public / libmde / md-menu.css

1
.md-menu {
2
 display: none;
3
 z-index: 2;
4
 box-sizing: border-box;
5
 cursor: default;
6
 padding: 0.25rem 0;
7
 border-radius:
8
  var(--md-sys-shape-corner-extra-small-default-size);
9
 background-color: var(--md-sys-color-surface-container-low);
10
 box-shadow: var(--md-box_shadow_level2);
11
 transform: translateY(-50%) scaleY(0);
12
 transition-timing-function:
13
  cubic-bezier(var(--md-sys-motion-easing-standard-x0),
14
   var(--md-sys-motion-easing-standard-y0),
15
   var(--md-sys-motion-easing-standard-x1),
16
   var(--md-sys-motion-easing-standard-y1));
17
 transition-property: display, transform;
18
 transition-behavior: allow-discrete;
19
 transition-duration: var(--md-sys-motion-duration-500);
20
}
21
22
.md-menu.open {
23
 display: block;
24
 transform: translateY(0) scaleY(1);
25
}
26
27
@starting-style {
28
 .md-menu.open {
29
  display: block;
30
  transform: translateY(-50%) scaleY(0);
31
 }
32
}
33
34
/* container */
35
.md-menu>*::after {
36
 content: "";
37
 position: absolute;
38
 z-index: -2;
39
 top: 0;
40
 right: 0;
41
 left: 0;
42
 bottom: 0;
43
}
44
45
/* container */
46
.md-menu>.selected::after {
47
 background-color: var(--md-sys-color-secondary-container);
48
}
49
50
/* label, shape */
51
.md-menu>* {
52
 position: relative;
53
 display: block;
54
 box-sizing: border-box;
55
 height: 3rem;
56
 line-height: 3rem;
57
 padding: 0 0.75rem;
58
 color: var(--md-sys-color-on-surface);
59
 font-family: var(--md-sys-typescale-label-large-font);
60
 font-weight: var(--md-sys-typescale-label-large-weight);
61
 font-size: var(--md-sys-typescale-label-large-size);
62
 font-style: var(--md-sys-typescale-label-large-font-style);
63
 letter-spacing: var(--md-sys-typescale-label-large-tracking);
64
 text-transform: var(--md-sys-typescale-label-large-text-transform);
65
 text-decoration: var(--md-sys-typescale-label-large-text-decoration);
66
 white-space: nowrap;
67
 text-overflow: ellipsis;
68
 overflow: hidden;
69
}
70
71
/* label, shape */
72
.md-menu>.selected {
73
 color: var(--md-sys-color-on-secondary-container);
74
}
75
76
/* state layer */
77
.md-menu>*::before {
78
 content: "";
79
 position: absolute;
80
 z-index: -1;
81
 top: 0;
82
 right: 0;
83
 left: 0;
84
 bottom: 0;
85
}
86
87
/* icon */
88
.md-menu>* span {
89
 position: relative;
90
 margin-right: 0.75rem;
91
 vertical-align: middle;
92
 color: var(--md-sys-color-on-surface-variant);
93
 font-size: 1.5rem;
94
 width: 1.5rem;
95
 height: 1.5rem;
96
}
97
98
/* icon */
99
.md-menu>.selected span {
100
 color: var(--md-sys-color-on-secondary-container);
101
}
102
103
/* state layer */
104
.md-menu>:hover::before {
105
 background-color: var(--md-sys-color-on-surface);
106
 opacity: var(--md-sys-state-hover-state-layer-opacity);
107
}
108
109
/* label, shape */
110
.md-menu>:hover {
111
 color: var(--md-sys-color-on-surface);
112
}
113
114
/* icon */
115
.md-menu>:hover span {
116
 color: var(--md-sys-color-on-surface-variant);
117
}
118
119
/* state layer */
120
.md-menu>:focus::before {
121
 background-color: var(--md-sys-color-on-surface);
122
 opacity: var(--md-sys-state-focus-state-layer-opacity);
123
}
124
125
/* label, shape */
126
.md-menu>:focus {
127
 color: var(--md-sys-color-on-surface);
128
 outline: none;
129
}
130
131
/* icon */
132
.md-menu>:focus span {
133
 color: var(--md-sys-color-on-surface-variant);
134
}
135
136
/* label, shape */
137
.md-menu>:active {
138
 background-position: center;
139
 background-image:
140
  radial-gradient(circle, var(--md-riple-color) 1%, transparent 1%);
141
 background-size: 100%;
142
 animation-name: md-ripple;
143
 animation-duration: var(--md-sys-motion-duration-500);
144
 color: var(--md-sys-color-on-surface);
145
}
146
147
/* state layer */
148
.md-menu>:active::before {
149
 background-color: var(--md-sys-color-on-surface);
150
 opacity: var(--md-sys-state-pressed-state-layer-opacity);
151
}
152
153
154
/* icon */
155
.md-menu>:active span {
156
 color: var(--md-sys-color-on-surface-variant);
157
}
158
159
.md-menu input[type="radio"] {
160
 appearance: none;
161
 transform: scaleX(0);
162
}

6. public / libmde / md-options-menu.js

1
import { abreElementoHtml } from "../libclienteweb/abreElementoHtml.js"
2
import { cierraElementoHtmo } from "../libclienteweb/cierraElementoHtmo.js"
3
import { querySelector } from "../libclienteweb/querySelector.js"
4
5
export class MdOptionsMenu extends HTMLElement {
6
7
 getContent() {
8
  return /* HTML */`
9
10
   <style>
11
12
    :host {
13
     position: absolute;
14
    }
15
16
   </style>
17
18
   <slot></slot>`
19
 }
20
21
 constructor() {
22
  super()
23
  const shadow = this.attachShadow({ mode: "open" })
24
  shadow.innerHTML = this.getContent()
25
  this._configuraOpciones = this._configuraOpciones.bind(this)
26
27
  /**
28
   * @private
29
   * @type { HTMLSlotElement }
30
   */
31
  this._slot = querySelector(shadow, "slot")
32
  /**
33
   * @private
34
   * @type { HTMLElement[] }
35
   */
36
  this._opciones = []
37
  this._slot.addEventListener("slotchange", this._configuraOpciones)
38
 }
39
40
 connectedCallback() {
41
  this.classList.add("md-menu")
42
  this.role = "listbox"
43
 }
44
45
 /**
46
  * @returns {readonly Readonly<HTMLElement>[]}
47
  */
48
 get opciones() {
49
  return this._opciones
50
 }
51
52
 get seleccion() {
53
  /** @type { HTMLInputElement | null } */
54
  const seleccionado = this.querySelector(".selected")
55
  return seleccionado === null ? "" : seleccionado.value
56
 }
57
58
 _configuraOpciones() {
59
  /**
60
   * @type {HTMLElement[]}
61
  */
62
  const opciones = []
63
  for (const opcion of this._slot.assignedElements()) {
64
   opcion.role = "option"
65
   if (opcion instanceof HTMLElement) {
66
    opciones.push(opcion)
67
   }
68
  }
69
  this._opciones = opciones
70
 }
71
72
 abre() {
73
  abreElementoHtml(this)
74
 }
75
76
77
 cierra() {
78
  cierraElementoHtmo(this)
79
 }
80
81
 /**
82
  * @param {string} value
83
  */
84
 muestraValue(value) {
85
  let texto = ""
86
  for (const opcion of this._opciones) {
87
   if (opcion.dataset.value === value) {
88
    opcion.classList.add("selected")
89
    let textContent = opcion.textContent
90
    if (texto === "" && textContent !== null) {
91
     textContent = textContent.trim()
92
     if (textContent !== "") {
93
      texto = textContent
94
     }
95
    }
96
   } else {
97
    opcion.classList.remove("selected")
98
   }
99
  }
100
  return texto
101
 }
102
103
}
104
105
customElements.define("md-options-menu", MdOptionsMenu)

7. public / libmde / md-outline-button.css

1
.md-outline-button {
2
 position: relative;
3
 box-sizing: border-box;
4
 border-radius: 1.25rem;
5
 height: 2.5rem;
6
 padding: 0 1.5rem;
7
 border: 0.0625rem solid var(--md-sys-color-outline);
8
 background-color: transparent;
9
 box-shadow: var(--md-box_shadow_level0);
10
 font-family: var(--md-sys-typescale-label-large-font);
11
 font-weight: var(--md-sys-typescale-label-large-weight);
12
 font-size: var(--md-sys-typescale-label-large-size);
13
 font-style: var(--md-sys-typescale-label-large-font-style);
14
 letter-spacing: var(--md-sys-typescale-label-large-tracking);
15
 text-transform: var(--md-sys-typescale-label-large-text-transform);
16
 text-decoration: var(--md-sys-typescale-label-large-text-decoration);
17
 color: var(--md-sys-color-primary);
18
 white-space: nowrap;
19
 text-overflow: ellipsis;
20
 overflow: hidden;
21
}
22
23
/* state layer */
24
.md-outline-button::after {
25
 content: "";
26
 position: absolute;
27
 z-index: -1;
28
 top: 0;
29
 right: 0;
30
 left: 0;
31
 bottom: 0;
32
 background-color: transparent;
33
}
34
35
.md-outline-button:hover {
36
 color: var(--md-sys-color-primary);
37
 border-color: var(--md-sys-color-outline);
38
}
39
40
/* state layer */
41
.md-outline-button:hover::after {
42
 background-color: var(--md-sys-color-primary);
43
 opacity: var(--md-sys-state-hover-state-layer-opacity);
44
}
45
46
.md-outline-button:focus {
47
 outline: none;
48
 color: var(--md-sys-color-primary);
49
 border-color: var(--md-sys-color-outline);
50
}
51
52
/* state layer */
53
.md-outline-button:focus::after {
54
 background-color: var(--md-sys-color-primary);
55
 opacity: var(--md-sys-state-focus-state-layer-opacity);
56
}
57
58
.md-outline-button:active {
59
 color: var(--md-sys-color-primary);
60
 border-color: var(--md-sys-color-outline);
61
 background-position: center;
62
 background-image:
63
  radial-gradient(circle, var(--md-riple-color) 1%, transparent 1%);
64
 background-size: 100%;
65
 animation-name: md-ripple;
66
 animation-duration: var(--md-sys-motion-duration-500);
67
 box-shadow: var(--md-box_shadow_level0) !important;
68
}
69
70
/* state layer */
71
.md-outline-button:active::after {
72
 background-color: var(--md-sys-color-primary);
73
 opacity: var(--md-sys-state-pressed-state-layer-opacity);
74
}
75
76
.md-outline-button:disabled {
77
 background-color: transparent !important;
78
 border-color: var(--md-sys-color-on-surface) !important;
79
 color: var(--md-sys-color-on-surface) !important;
80
 opacity: 0.38 !important;
81
}
82
83
/* container */
84
.md-outline-button:disabled::after {
85
 background-color: transparent !important;
86
 opacity: 1 !important;
87
}

8. public / libmde / md-select-menu.js

1
import { getAttribute } from "../libclienteweb/getAttribute.js"
2
import { querySelector } from "../libclienteweb/querySelector.js"
3
import { MdOptionsMenu } from "./md-options-menu.js"
4
5
export class MdSelectMenu extends HTMLElement {
6
7
 static get observedAttributes() {
8
  return ["options", "value", "required"]
9
 }
10
11
 getContent() {
12
  return /* HTML */ `
13
   <link rel="stylesheet" href="/css/material-symbols-outlined.css">
14
15
   <style>
16
    :host {
17
     display: block;
18
     cursor: default;
19
    }
20
21
    output {
22
     display: block;
23
     padding-right: 2rem;
24
     white-space: nowrap;
25
     text-overflow: ellipsis;
26
     overflow: hidden;
27
    }
28
29
    #up {
30
     position: absolute;
31
     bottom: 0.5rem;
32
     right: 0.75rem;
33
     display: none;
34
     color: var(--md-sys-color-on-surface-variant);
35
    }
36
37
    #down {
38
     position: absolute;
39
     bottom: 0.5rem;
40
     right: 0.75rem;
41
     color: var(--md-sys-color-on-surface-variant);
42
    }
43
44
    :host(.open) #up {
45
     display: inline-block;
46
    }
47
48
    :host(.open) #down {
49
     display: none;
50
    }
51
52
    :host(:invalid) #up,
53
    :host(:invalid) #down {
54
     color: var(--md-sys-color-error);
55
    }
56
57
   </style>
58
   <output></output>
59
   <span id="down" class="material-symbols-outlined">
60
    arrow_drop_down
61
   </span>
62
   <span id="up" class="material-symbols-outlined">
63
    arrow_drop_up
64
   </span>`
65
 }
66
67
 constructor() {
68
  super()
69
70
  const shadow = this.attachShadow({ mode: "open" })
71
  shadow.innerHTML = this.getContent()
72
73
  this._alterna = this._alterna.bind(this)
74
  this._onKeyDown = this._onKeyDown.bind(this)
75
  this._cierra = this._cierra.bind(this)
76
  this._clicEnDialogo = this._clicEnDialogo.bind(this)
77
  this.clicExterno = this.clicExterno.bind(this)
78
  this.muestraValue = this.muestraValue.bind(this)
79
80
  /**
81
   * @private
82
   * @type {string}
83
   */
84
  this._customValidity = ""
85
86
  /**
87
   * @private
88
   * @type { HTMLOutputElement }
89
   */
90
  this.output = querySelector(shadow, "output")
91
  /**
92
   * @private
93
   * @type { MdOptionsMenu | null }
94
   */
95
  this._optionsMenu = null
96
  /**
97
   * @protected
98
   * @readonly
99
   */
100
  this._internals = this.attachInternals()
101
  this._internals.role = "select"
102
  addEventListener("load", this.muestraValue)
103
 }
104
105
 connectedCallback() {
106
  this.tabIndex = 0
107
  this.role = "combobox"
108
  this.ariaHasPopup = "listbox"
109
  this.ariaExpanded = "false"
110
  this["aria-controls"] = this.options
111
  this.addEventListener("keydown", this._onKeyDown)
112
  const parentElement = this.parentElement
113
  if (parentElement !== null) {
114
   parentElement.addEventListener("click", this._alterna)
115
  }
116
 }
117
118
 /**
119
  * @param {string} nombreDeAtributo
120
  * @param {string} _valorAnterior
121
  * @param {string} _nuevoValor
122
  */
123
 attributeChangedCallback(nombreDeAtributo, _valorAnterior, _nuevoValor) {
124
  switch (nombreDeAtributo) {
125
   case "options":
126
    this._cambiaOptions()
127
    break
128
   case "value":
129
    this.muestraValue()
130
    break
131
   case "required":
132
    this._checkValidity()
133
    break
134
  }
135
 }
136
137
 get options() {
138
  return getAttribute(this, "options")
139
 }
140
141
 set options(options) {
142
  this.setAttribute("options", options)
143
 }
144
145
 _cambiaOptions() {
146
  if (this._optionsMenu !== null) {
147
   this._optionsMenu = null
148
  }
149
  this["aria-controls"] = this.options
150
 }
151
152
 get required() {
153
  return this.hasAttribute("required")
154
 }
155
156
 set required(required) {
157
  this.toggleAttribute("required", Boolean(required))
158
 }
159
160
 get value() {
161
  return getAttribute(this, "value")
162
 }
163
164
 set value(value) {
165
  this.setAttribute("value", value)
166
 }
167
168
 get name() {
169
  return getAttribute(this, "name")
170
 }
171
172
 set name(name) {
173
  this.setAttribute("name", name)
174
 }
175
176
 muestraValue() {
177
  const value = this.value
178
  this._internals.setFormValue(value)
179
180
  // En un futuro se usará esto en vez de la clase populated.
181
  // if (value === "") {
182
  //  this._internals.states.delete("populated")
183
  // } else {
184
  //  this._internals.states.add("populated")
185
  // }
186
187
  if (this.isConnected) {
188
   if (value === "") {
189
    this.classList.remove("populated")
190
   } else {
191
    this.classList.add("populated")
192
   }
193
   this._checkValidity()
194
   const optionsMenu = this.optionsMenu
195
   if (optionsMenu !== null) {
196
    this.output.value = optionsMenu.muestraValue(value)
197
   }
198
  }
199
 }
200
201
 get form() {
202
  return this._internals && this._internals.form
203
 }
204
205
 get willValidate() {
206
  return this._internals ? this._internals.willValidate : true
207
 }
208
209
 /**
210
  * @param {string} message
211
  */
212
 setCustomValidity(message) {
213
  this._customValidity = message
214
  this._checkValidity()
215
 }
216
217
 /**
218
  * @returns {ValidityState}
219
  */
220
 get validity() {
221
  return this._internals.validity
222
 }
223
224
 checkValidity() {
225
  return this._internals.checkValidity()
226
 }
227
228
 reportValidity() {
229
  return this._internals.reportValidity()
230
 }
231
232
 get validationMessage() {
233
  return this._internals.validationMessage
234
 }
235
 /** @returns {boolean} */
236
 _checkValidity() {
237
  if (this._customValidity !== "") {
238
   this._internals.setValidity({ customError: true }, this._customValidity)
239
   return false
240
  } else if (this.required && this.value === "") {
241
   this._internals.setValidity({ valueMissing: true }, "Seleccione una opción.")
242
   return false
243
  } else {
244
   this._internals.setValidity({})
245
   return true
246
  }
247
 }
248
249
 /** @private */
250
 _alterna() {
251
  if (this.classList.contains("open")) {
252
   this._cierra()
253
  } else {
254
   this._abre()
255
  }
256
 }
257
258
 /** @private */
259
 _abre() {
260
  this.classList.add("open")
261
  const bounds = this.getBoundingClientRect()
262
   const optionsMenu = this.optionsMenu
263
   if (optionsMenu !== null) {
264
    optionsMenu.style.top = `${ bounds.bottom}px`
265
    optionsMenu.style.left = `${ bounds.left}px`
266
    optionsMenu.style.width = `${ bounds.width}px`
267
    optionsMenu.abre()
268
    this.focus()
269
    optionsMenu.addEventListener("click", this._clicEnDialogo)
270
   }
271
   this.ariaExpanded = "true"
272
   document.addEventListener("click", this.clicExterno)
273
 }
274
275
 /** @private */
276
 _cierra() {
277
  this.classList.remove("open")
278
  const optionsMenu = this.optionsMenu
279
  if (optionsMenu !== null) {
280
   optionsMenu.cierra()
281
   optionsMenu.removeEventListener("click", this._clicEnDialogo)
282
  }
283
  this.ariaExpanded = "false"
284
  document.removeEventListener("click", this.clicExterno)
285
  this.dispatchEvent(new Event("input", { bubbles: true }))
286
 }
287
288
 get optionsMenu() {
289
  if (this._optionsMenu === null) {
290
   if (this.options !== "") {
291
    const optionsMenu = document.getElementById(this.options)
292
    if (optionsMenu instanceof MdOptionsMenu) {
293
     this._optionsMenu = optionsMenu
294
    } else {
295
     throw new Error(`Valor incorrecto para options: "${this.options}".`)
296
    }
297
   }
298
  }
299
  return this._optionsMenu
300
 }
301
302
 /** @private */
303
 _avanzaOpcion() {
304
  const i = this._valueIndex
305
  if (i > -1) {
306
   const optionsMenu = this.optionsMenu
307
   if (optionsMenu !== null) {
308
    const opciones = optionsMenu.opciones
309
    if (i < opciones.length - 1) {
310
     this.value = getAttribute(opciones[i + 1], "data-value")
311
    }
312
   }
313
  }
314
 }
315
316
 /** @private */
317
 _retrocedeOpcion() {
318
  const i = this._valueIndex
319
  if (i > -1) {
320
   const optionsMenu = this.optionsMenu
321
   if (optionsMenu !== null) {
322
    const opciones = optionsMenu.opciones
323
    if (i > 0) {
324
     this.value = getAttribute(opciones[i - 1], "data-value")
325
    }
326
   }
327
  }
328
 }
329
330
 /**
331
  * @private
332
  * @returns {number}
333
  */
334
 get _valueIndex() {
335
  const value = this.value
336
  const optionsMenu = this.optionsMenu
337
  return (optionsMenu === null
338
   ? -1
339
   : optionsMenu.opciones.findIndex(opcion => opcion.dataset.value === value))
340
 }
341
342
 /**
343
  * @private
344
  * @param {Event} event
345
  */
346
 _clicEnDialogo(event) {
347
  const target = event.target
348
  const optionsMenu = this.optionsMenu
349
  let value = ""
350
  if (optionsMenu !== null) {
351
   for (const opcion of optionsMenu.opciones) {
352
    if (opcion === target) {
353
     opcion.classList.add("selected")
354
     value = getAttribute(opcion, "data-value")
355
    } else {
356
     opcion.classList.remove("selected")
357
    }
358
   }
359
  }
360
  this.value = value
361
  this._cierra()
362
  this.focus()
363
}
364
365
 /**
366
  * @param {Event} evt
367
  */
368
 clicExterno(evt) {
369
  const target = evt.target
370
  const parentElement = this.parentElement
371
  const optionsMenu = this._optionsMenu
372
  if (this.classList.contains("open")
373
   && target instanceof HTMLElement
374
   && parentElement !== null
375
   && !parentElement.contains(target)
376
   && optionsMenu !== null
377
   && !optionsMenu.contains(target)) {
378
   this._cierra()
379
  }
380
 }
381
382
 /**
383
  * @param { KeyboardEvent } event
384
  */
385
 _onKeyDown(event) {
386
  const key = event.key
387
  const optionsMenu = this._optionsMenu
388
  if (optionsMenu !== null) {
389
   if (optionsMenu.classList.contains("open")) {
390
    if (key === "ArrowDown") {
391
     event.preventDefault()
392
     this._avanzaOpcion()
393
    } else if (key === "ArrowUp") {
394
     event.preventDefault()
395
     this._retrocedeOpcion()
396
    } else if (key === "Escape") {
397
     event.preventDefault()
398
     this._cierra()
399
    } else if (key === " ") {
400
     event.preventDefault()
401
     this._cierra()
402
    } else if (key === "Tab") {
403
     this._cierra()
404
    } else {
405
     event.preventDefault()
406
    }
407
   } else if (key === " ") {
408
    event.preventDefault()
409
    this._abre()
410
   } else if (key === "Tab") {
411
    this._cierra()
412
   } else {
413
    event.preventDefault()
414
   }
415
  }
416
 }
417
418
}
419
420
MdSelectMenu.formAssociated = true
421
422
customElements.define("md-select-menu", MdSelectMenu)

9. public / libmde / md-tab.css

1
.md-tab {
2
 display: flex;
3
 background-color: transparent;
4
 align-items: stretch;
5
 flex-wrap: nowrap;
6
 overflow-x: auto;
7
 position: sticky;
8
 z-index: 1;
9
}
10
11
.md-tab.fixed {
12
 justify-content: center;
13
}
14
15
.md-tab.scrollable {
16
 padding-left: 2rem;
17
 gap: 1rem;
18
}
19
20
.md-tab.scroll {
21
 background-color: var(--md-sys-color-surface-container-low);
22
}
23
24
.md-tab a {
25
 position: relative;
26
 display: flex;
27
 flex-direction: column;
28
 justify-content: start;
29
 align-items: center;
30
 color: var(--md-sys-color-on-surface-variant);
31
 font-family: var(--md-sys-typescale-title-small-font);
32
 font-weight: var(--md-sys-typescale-title-small-weight);
33
 font-size: var(--md-sys-typescale-title-small-size);
34
 font-style: var(--md-sys-typescale-title-small-font-style);
35
 letter-spacing: var(--md-sys-typescale-title-small-tracking);
36
 line-height: var(--md-sys-typescale-title-small-line-height);
37
 text-transform: var(--md-sys-typescale-title-small-text-transform);
38
 text-decoration: var(--md-sys-typescale-title-small-text-decoration);
39
 text-align: center;
40
 box-sizing: border-box;
41
 border-bottom: 0.1875rem solid var(--md-sys-color-surface);
42
}
43
44
.md-tab.fixed a {
45
 flex: 0 0 var(--tabWidth);
46
}
47
48
.md-tab.scrollable a {
49
 flex: 0 0 auto;
50
}
51
52
.md-tab a.active {
53
 border-bottom-color: var(--md-sys-color-primary);
54
}
55
56
/* state layer */
57
.md-tab a::after {
58
 content: "";
59
 position: absolute;
60
 z-index: -1;
61
 top: 0;
62
 right: 0;
63
 left: 0;
64
 bottom: 0;
65
 background-color: transparent;
66
}
67
68
.md-tab span {
69
 font-size: var(--iconSize);
70
 height: var(--iconSize);
71
 width: var(--iconSize);
72
 color: var(--md-sys-color-on-surface-variant);
73
}
74
75
.md-tab .active span {
76
 color: var(--md-sys-color-primary);
77
 font-variation-settings: 'FILL' 1, 'wght' 700, 'GRAD' 0, 'opsz' 48;
78
}
79
80
.md-tab a:hover {
81
 color: var(--md-sys-color-on-surface);
82
}
83
84
/* state layer */
85
.md-tab a:hover::after {
86
 background-color: var(--md-sys-color-on-surface);
87
 opacity: var(--md-sys-state-hover-state-layer-opacity);
88
}
89
90
.md-tab a.active:hover {
91
 color: var(--md-sys-color-primary);
92
}
93
94
/* state layer */
95
.md-tab a.active:hover::after {
96
 background-color: var(--md-sys-color-primary);
97
 opacity: var(--md-sys-state-hover-state-layer-opacity);
98
}
99
100
.md-tab a:hover span {
101
 color: var(--md-sys-color-on-surface);
102
}
103
104
.md-tab a.active:hover span {
105
 color: var(--md-sys-color-primary);
106
}
107
108
.md-tab a:focus {
109
 outline: none;
110
}
111
112
/* state layer */
113
.md-tab a:focus::after {
114
 background-color: var(--md-sys-color-on-surface);
115
 opacity: var(--md-sys-state-focus-state-layer-opacity);
116
}
117
118
/* state layer */
119
.md-tab a.active:focus::after {
120
 background-color: var(--md-sys-color-primary);
121
 opacity: var(--md-sys-state-hover-state-layer-opacity);
122
}
123
124
.md-tab a:active {
125
 background-position: center;
126
 background-image:
127
   radial-gradient(circle, var(--md-riple-color) 1%, transparent 1%);
128
 background-size: 100%;
129
 animation-name: md-ripple;
130
 animation-duration: var(--md-sys-motion-duration-500);
131
}
132
133
/* state layer */
134
.md-tab a:active::after {
135
 background-color: var(--md-sys-color-on-surface);
136
 opacity: var(--md-sys-state-pressed-state-layer-opacity);
137
}
138
139
/* state layer */
140
.md-tab a.active:active::after {
141
 background-color: var(--md-sys-color-primary);
142
 opacity: var(--md-sys-state-pressed-state-layer-opacity);
143
}

O. Carpeta « public / material-tokens »

1. Carpeta « public / material-tokens / css »

A. public / material-tokens / css / baseline.css

1
/*
2
 Copyright 2016 Google Inc. All rights reserved.
3
4
 Licensed under the Apache License, Version 2.0 (the "License");
5
 you may not use this file except in compliance with the License.
6
 You may obtain a copy of the License at
7
8
     http://www.apache.org/licenses/LICENSE-2.0
9
10
 Unless required by applicable law or agreed to in writing, software
11
 distributed under the License is distributed on an "AS IS" BASIS,
12
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 See the License for the specific language governing permissions and
14
 limitations under the License.
15
*/
16
17
@import url(palette.css);
18
@import url(typography.css);
19
@import url(colors.css);
20
@import url(shape.css);
21
@import url(motion.css);
22
@import url(state.css);
23
@import url(elevation.css);
24
@import url(theme/light.css) screen and (prefers-color-scheme: light);
25
@import url(theme/dark.css) screen and (prefers-color-scheme: dark);
26

B. public / material-tokens / css / colors.css

1
/*
2
 Copyright 2016 Google Inc. All rights reserved.
3
4
 Licensed under the Apache License, Version 2.0 (the "License");
5
 you may not use this file except in compliance with the License.
6
 You may obtain a copy of the License at
7
8
     http://www.apache.org/licenses/LICENSE-2.0
9
10
 Unless required by applicable law or agreed to in writing, software
11
 distributed under the License is distributed on an "AS IS" BASIS,
12
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 See the License for the specific language governing permissions and
14
 limitations under the License.
15
*/
16
17
.primary {
18
  color: var(--md-sys-color-on-primary);
19
  background-color: var(--md-sys-color-primary);
20
}
21
.on-primary {
22
  color: var(--md-sys-color-primary);
23
  background-color: var(--md-sys-color-on-primary);
24
}
25
.primary-container {
26
  color: var(--md-sys-color-on-primary-container);
27
  background-color: var(--md-sys-color-primary-container);
28
}
29
.on-primary-container {
30
  color: var(--md-sys-color-primary-container);
31
  background-color: var(--md-sys-color-on-primary-container);
32
}
33
.secondary {
34
  color: var(--md-sys-color-on-secondary);
35
  background-color: var(--md-sys-color-secondary);
36
}
37
.on-secondary {
38
  color: var(--md-sys-color-secondary);
39
  background-color: var(--md-sys-color-on-secondary);
40
}
41
.secondary-container {
42
  color: var(--md-sys-color-on-secondary-container);
43
  background-color: var(--md-sys-color-secondary-container);
44
}
45
.on-secondary-container {
46
  color: var(--md-sys-color-secondary-container);
47
  background-color: var(--md-sys-color-on-secondary-container);
48
}
49
.tertiary {
50
  color: var(--md-sys-color-on-tertiary);
51
  background-color: var(--md-sys-color-tertiary);
52
}
53
.on-tertiary {
54
  color: var(--md-sys-color-tertiary);
55
  background-color: var(--md-sys-color-on-tertiary);
56
}
57
.tertiary-container {
58
  color: var(--md-sys-color-on-tertiary-container);
59
  background-color: var(--md-sys-color-tertiary-container);
60
}
61
.on-tertiary-container {
62
  color: var(--md-sys-color-tertiary-container);
63
  background-color: var(--md-sys-color-on-tertiary-container);
64
}
65
.background {
66
  color: var(--md-sys-color-on-background);
67
  background-color: var(--md-sys-color-background);
68
}
69
.surface {
70
  color: var(--md-sys-color-on-surface);
71
  background-color: var(--md-sys-color-surface);
72
}
73
.surface-variant {
74
  color: var(--md-sys-color-on-surface-variant);
75
  background-color: var(--md-sys-color-surface-variant);
76
}
77
.on-surface-variant {
78
  color: var(--md-sys-color-surface-variant);
79
  background-color: var(--md-sys-color-on-surface-variant);
80
}
81
.outline {
82
  border: 1px solid var(--md-sys-color-outline);
83
}
84
.inverse-surface {
85
  color: var(--md-sys-color-on-inverse-surface);
86
  background-color: var(--md-sys-color-inverse-surface);
87
}
88
.on-inverse-surface {
89
  color: var(--md-sys-color-inverse-surface);
90
  background-color: var(--md-sys-color-on-inverse-surface);
91
}
92
.inverse-primary {
93
  color: var(--md-sys-color-on-inverse-primary);
94
  background-color: var(--md-sys-color-inverse-primary);
95
}
96
.on-inverse-primary {
97
  color: var(--md-sys-color-inverse-primary);
98
  background-color: var(--md-sys-color-on-inverse-primary);
99
}
100
.surface-tint {
101
  background-color: var(--md-sys-color-on-surface-tint);
102
}
103
.error {
104
  color: var(--md-sys-color-on-error);
105
  background-color: var(--md-sys-color-error);
106
}
107
.on-error {
108
  color: var(--md-sys-color-error);
109
  background-color: var(--md-sys-color-on-error);
110
}
111
.error-container {
112
  color: var(--md-sys-color-on-error-container);
113
  background-color: var(--md-sys-color-error-container);
114
}
115
.on-error-container {
116
  color: var(--md-sys-color-error-container);
117
  background-color: var(--md-sys-color-on-error-container);
118
}
119
.black {
120
  background-color: var(--md-ref-palette-black);
121
}
122
.black-text {
123
  color: var(--md-ref-palette-black);
124
}
125
.white {
126
  background-color: var(--md-ref-palette-white);
127
}
128
.white-text {
129
  color: var(--md-ref-palette-white);
130
}
131

C. public / material-tokens / css / elevation.css

1
/*
2
 Copyright 2016 Google Inc. All rights reserved.
3
4
 Licensed under the Apache License, Version 2.0 (the "License");
5
 you may not use this file except in compliance with the License.
6
 You may obtain a copy of the License at
7
8
     http://www.apache.org/licenses/LICENSE-2.0
9
10
 Unless required by applicable law or agreed to in writing, software
11
 distributed under the License is distributed on an "AS IS" BASIS,
12
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 See the License for the specific language governing permissions and
14
 limitations under the License.
15
*/
16
17
:root {
18
  /* Surface tint color */
19
  --md-sys-elevation-surface-tint-color: var(--md-sys-color-primary);
20
  /* +5 */
21
  --md-sys-elevation-level5-value: 12px;
22
  --md-sys-elevation-level5-unit: 1px;
23
  --md-sys-elevation-level5: 12px;
24
  /* +4 */
25
  --md-sys-elevation-level4-value: 8px;
26
  --md-sys-elevation-level4-unit: 1px;
27
  --md-sys-elevation-level4: 8px;
28
  /* +3 */
29
  --md-sys-elevation-level3-value: 6px;
30
  --md-sys-elevation-level3-unit: 1px;
31
  --md-sys-elevation-level3: 6px;
32
  /* +2 */
33
  --md-sys-elevation-level2-value: 3px;
34
  --md-sys-elevation-level2-unit: 1px;
35
  --md-sys-elevation-level2: 3px;
36
  /* +1 */
37
  --md-sys-elevation-level1-value: 1px;
38
  --md-sys-elevation-level1-unit: 1px;
39
  --md-sys-elevation-level1: 1px;
40
  /* 0 */
41
  --md-sys-elevation-level0-value: 0px;
42
  --md-sys-elevation-level0-unit: 1px;
43
  --md-sys-elevation-level0: 0px;
44
}
45
.elevation-0 {
46
  box-shadow: var(--md-sys-elevation-level0);
47
}
48
.elevation-1 {
49
  box-shadow: var(--md-sys-elevation-level1);
50
}
51
.elevation-2 {
52
  box-shadow: var(--md-sys-elevation-level2);
53
}
54
.elevation-3 {
55
  box-shadow: var(--md-sys-elevation-level3);
56
}
57
.elevation-4 {
58
  box-shadow: var(--md-sys-elevation-level4);
59
}
60
.elevation-5 {
61
  box-shadow: var(--md-sys-elevation-level5);
62
}
63

D. public / material-tokens / css / motion.css

1
/*
2
 Copyright 2016 Google Inc. All rights reserved.
3
4
 Licensed under the Apache License, Version 2.0 (the "License");
5
 you may not use this file except in compliance with the License.
6
 You may obtain a copy of the License at
7
8
     http://www.apache.org/licenses/LICENSE-2.0
9
10
 Unless required by applicable law or agreed to in writing, software
11
 distributed under the License is distributed on an "AS IS" BASIS,
12
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 See the License for the specific language governing permissions and
14
 limitations under the License.
15
*/
16
17
:root {
18
  /* Emphasized decelerate easing (out) */
19
  --md-sys-motion-easing-emphasized-decelerate-x0: 0.05000000074505806;
20
  --md-sys-motion-easing-emphasized-decelerate-y0: 0.699999988079071;
21
  --md-sys-motion-easing-emphasized-decelerate-x1: 0.10000000149011612;
22
  --md-sys-motion-easing-emphasized-decelerate-y1: 1;
23
  /* Emphasized accelerate easing (in) */
24
  --md-sys-motion-easing-emphasized-accelerate-x0: 0.30000001192092896;
25
  --md-sys-motion-easing-emphasized-accelerate-y0: 0;
26
  --md-sys-motion-easing-emphasized-accelerate-x1: 0.800000011920929;
27
  --md-sys-motion-easing-emphasized-accelerate-y1: 0.15000000596046448;
28
  /* Standard decelerate easing (out) */
29
  --md-sys-motion-easing-standard-decelerate-x0: 0;
30
  --md-sys-motion-easing-standard-decelerate-y0: 0;
31
  --md-sys-motion-easing-standard-decelerate-x1: 0;
32
  --md-sys-motion-easing-standard-decelerate-y1: 1;
33
  /* Standard accelerate easing (in) */
34
  --md-sys-motion-easing-standard-accelerate-x0: 0.30000001192092896;
35
  --md-sys-motion-easing-standard-accelerate-y0: 0;
36
  --md-sys-motion-easing-standard-accelerate-x1: 1;
37
  --md-sys-motion-easing-standard-accelerate-y1: 1;
38
  /* Duration 1000ms */
39
  --md-sys-motion-duration-1000: 1000ms;
40
  /* Duration 900ms */
41
  --md-sys-motion-duration-900: 900ms;
42
  /* Duration 800ms */
43
  --md-sys-motion-duration-800: 800ms;
44
  /* Duration 700ms */
45
  --md-sys-motion-duration-700: 700ms;
46
  /* Duration 600ms */
47
  --md-sys-motion-duration-600: 600ms;
48
  /* Duration 550ms */
49
  --md-sys-motion-duration-550: 550ms;
50
  /* Duration 500ms */
51
  --md-sys-motion-duration-500: 500ms;
52
  /* Duration 450ms */
53
  --md-sys-motion-duration-450: 450ms;
54
  /* Duration 400ms */
55
  --md-sys-motion-duration-400: 400ms;
56
  /* Duration 350ms */
57
  --md-sys-motion-duration-350: 350ms;
58
  /* Duration 300ms */
59
  --md-sys-motion-duration-300: 300ms;
60
  /* Duration 250ms */
61
  --md-sys-motion-duration-250: 250ms;
62
  /* Duration 200ms */
63
  --md-sys-motion-duration-200: 200ms;
64
  /* Duration 150ms */
65
  --md-sys-motion-duration-150: 150ms;
66
  /* Duration 100ms */
67
  --md-sys-motion-duration-100: 100ms;
68
  /* Duration 50ms */
69
  --md-sys-motion-duration-50: 50ms;
70
  /* Standard easing (in and out) */
71
  --md-sys-motion-easing-standard-x0: 0.20000000298023224;
72
  --md-sys-motion-easing-standard-y0: 0;
73
  --md-sys-motion-easing-standard-x1: 0;
74
  --md-sys-motion-easing-standard-y1: 1;
75
  /* Linear easing */
76
  --md-sys-motion-easing-linear-x0: 0;
77
  --md-sys-motion-easing-linear-y0: 0;
78
  --md-sys-motion-easing-linear-x1: 1;
79
  --md-sys-motion-easing-linear-y1: 1;
80
  /* Emphasized */
81
  --md-sys-motion-easing-emphasized-x0: 0.20000000298023224;
82
  --md-sys-motion-easing-emphasized-y0: 0;
83
  --md-sys-motion-easing-emphasized-x1: 0;
84
  --md-sys-motion-easing-emphasized-y1: 1;
85
  /* Motion path */
86
  --md-sys-motion-path-standard-path: 1;
87
}
88
.duration-50 {
89
  transition-duration: var(--md-sys-motion-duration-50);
90
}
91
.duration-100 {
92
  transition-duration: var(--md-sys-motion-duration-100);
93
}
94
.duration-150 {
95
  transition-duration: var(--md-sys-motion-duration-150);
96
}
97
.duration-200 {
98
  transition-duration: var(--md-sys-motion-duration-200);
99
}
100
.duration-250 {
101
  transition-duration: var(--md-sys-motion-duration-250);
102
}
103
.duration-300 {
104
  transition-duration: var(--md-sys-motion-duration-300);
105
}
106
.duration-350 {
107
  transition-duration: var(--md-sys-motion-duration-350);
108
}
109
.duration-400 {
110
  transition-duration: var(--md-sys-motion-duration-400);
111
}
112
.duration-450 {
113
  transition-duration: var(--md-sys-motion-duration-450);
114
}
115
.duration-500 {
116
  transition-duration: var(--md-sys-motion-duration-500);
117
}
118
.duration-550 {
119
  transition-duration: var(--md-sys-motion-duration-550);
120
}
121
.duration-600 {
122
  transition-duration: var(--md-sys-motion-duration-600);
123
}
124
.duration-700 {
125
  transition-duration: var(--md-sys-motion-duration-700);
126
}
127
.duration-800 {
128
  transition-duration: var(--md-sys-motion-duration-800);
129
}
130
.duration-900 {
131
  transition-duration: var(--md-sys-motion-duration-900);
132
}
133
.duration-1000 {
134
  transition-duration: var(--md-sys-motion-duration-1000);
135
}
136
.easing-standard {
137
  transition-timing-function: cubic-bezier(
138
    var(--md-sys-motion-easing-standard-x0),
139
    var(--md-sys-motion-easing-standard-y0),
140
    var(--md-sys-motion-easing-standard-x1),
141
    var(--md-sys-motion-easing-standard-y1)
142
  );
143
}
144
.easing-linear {
145
  transition-timing-function: cubic-bezier(
146
    var(--md-sys-motion-easing-linear-x0),
147
    var(--md-sys-motion-easing-linear-y0),
148
    var(--md-sys-motion-easing-linear-x1),
149
    var(--md-sys-motion-easing-linear-y1)
150
  );
151
}
152
.easing-standard-accelerate {
153
  transition-timing-function: cubic-bezier(
154
    var(--md-sys-motion-easing-standard-accelerate-x0),
155
    var(--md-sys-motion-easing-standard-accelerate-y0),
156
    var(--md-sys-motion-easing-standard-accelerate-x1),
157
    var(--md-sys-motion-easing-standard-accelerate-y1)
158
  );
159
}
160
.easing-standard-decelerate {
161
  transition-timing-function: cubic-bezier(
162
    var(--md-sys-motion-easing-standard-decelerate-x0),
163
    var(--md-sys-motion-easing-standard-decelerate-y0),
164
    var(--md-sys-motion-easing-standard-decelerate-x1),
165
    var(--md-sys-motion-easing-standard-decelerate-y1)
166
  );
167
}
168
.easing-emphasized {
169
  transition-timing-function: cubic-bezier(
170
    var(--md-sys-motion-easing-emphasized-x0),
171
    var(--md-sys-motion-easing-emphasized-y0),
172
    var(--md-sys-motion-easing-emphasized-x1),
173
    var(--md-sys-motion-easing-emphasized-y1)
174
  );
175
}
176

E. public / material-tokens / css / palette.css

1
/*
2
 Copyright 2016 Google Inc. All rights reserved.
3
4
 Licensed under the Apache License, Version 2.0 (the "License");
5
 you may not use this file except in compliance with the License.
6
 You may obtain a copy of the License at
7
8
     http://www.apache.org/licenses/LICENSE-2.0
9
10
 Unless required by applicable law or agreed to in writing, software
11
 distributed under the License is distributed on an "AS IS" BASIS,
12
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 See the License for the specific language governing permissions and
14
 limitations under the License.
15
*/
16
17
:root {
18
  /* Error 0 */
19
  --md-ref-palette-error0: #000000ff;
20
  /* Error 10 */
21
  --md-ref-palette-error10: #410e0bff;
22
  /* Error 20 */
23
  --md-ref-palette-error20: #601410ff;
24
  /* Error 30 */
25
  --md-ref-palette-error30: #8c1d18ff;
26
  /* Error 40 */
27
  --md-ref-palette-error40: #b3261eff;
28
  /* Error 50 */
29
  --md-ref-palette-error50: #dc362eff;
30
  /* Error 60 */
31
  --md-ref-palette-error60: #e46962ff;
32
  /* Error 70 */
33
  --md-ref-palette-error70: #ec928eff;
34
  /* Error 80 */
35
  --md-ref-palette-error80: #f2b8b5ff;
36
  /* Error 90 */
37
  --md-ref-palette-error90: #f9dedcff;
38
  /* Error 95 */
39
  --md-ref-palette-error95: #fceeeeff;
40
  /* Error 99 */
41
  --md-ref-palette-error99: #fffbf9ff;
42
  /* Error 100 */
43
  --md-ref-palette-error100: #ffffffff;
44
  /* Tertiary 0 */
45
  --md-ref-palette-tertiary0: #000000ff;
46
  /* Tertiary 10 */
47
  --md-ref-palette-tertiary10: #31111dff;
48
  /* Tertiary 20 */
49
  --md-ref-palette-tertiary20: #492532ff;
50
  /* Tertiary 30 */
51
  --md-ref-palette-tertiary30: #633b48ff;
52
  /* Tertiary 40 */
53
  --md-ref-palette-tertiary40: #7d5260ff;
54
  /* Tertiary 50 */
55
  --md-ref-palette-tertiary50: #986977ff;
56
  /* Tertiary 60 */
57
  --md-ref-palette-tertiary60: #b58392ff;
58
  /* Tertiary 70 */
59
  --md-ref-palette-tertiary70: #d29dacff;
60
  /* Tertiary 80 */
61
  --md-ref-palette-tertiary80: #efb8c8ff;
62
  /* Tertiary 90 */
63
  --md-ref-palette-tertiary90: #ffd8e4ff;
64
  /* Tertiary 95 */
65
  --md-ref-palette-tertiary95: #ffecf1ff;
66
  /* Tertiary 99 */
67
  --md-ref-palette-tertiary99: #fffbfaff;
68
  /* Tertiary 100 */
69
  --md-ref-palette-tertiary100: #ffffffff;
70
  /* Secondary 0 */
71
  --md-ref-palette-secondary0: #000000ff;
72
  /* Secondary 10 */
73
  --md-ref-palette-secondary10: #1d192bff;
74
  /* Secondary 20 */
75
  --md-ref-palette-secondary20: #332d41ff;
76
  /* Secondary 30 */
77
  --md-ref-palette-secondary30: #4a4458ff;
78
  /* Secondary 40 */
79
  --md-ref-palette-secondary40: #625b71ff;
80
  /* Secondary 50 */
81
  --md-ref-palette-secondary50: #7a7289ff;
82
  /* Secondary 60 */
83
  --md-ref-palette-secondary60: #958da5ff;
84
  /* Secondary 70 */
85
  --md-ref-palette-secondary70: #b0a7c0ff;
86
  /* Secondary 80 */
87
  --md-ref-palette-secondary80: #ccc2dcff;
88
  /* Secondary 90 */
89
  --md-ref-palette-secondary90: #e8def8ff;
90
  /* Secondary 95 */
91
  --md-ref-palette-secondary95: #f6edffff;
92
  /* Secondary 99 */
93
  --md-ref-palette-secondary99: #fffbfeff;
94
  /* Secondary 100 */
95
  --md-ref-palette-secondary100: #ffffffff;
96
  /* Primary 0 */
97
  --md-ref-palette-primary0: #000000ff;
98
  /* Primary 10 */
99
  --md-ref-palette-primary10: #21005dff;
100
  /* Primary 20 */
101
  --md-ref-palette-primary20: #381e72ff;
102
  /* Primary 30 */
103
  --md-ref-palette-primary30: #4f378bff;
104
  /* Primary 40 */
105
  --md-ref-palette-primary40: #6750a4ff;
106
  /* Primary 50 */
107
  --md-ref-palette-primary50: #7f67beff;
108
  /* Primary 60 */
109
  --md-ref-palette-primary60: #9a82dbff;
110
  /* Primary 70 */
111
  --md-ref-palette-primary70: #b69df8ff;
112
  /* Primary 80 */
113
  --md-ref-palette-primary80: #d0bcffff;
114
  /* Primary 90 */
115
  --md-ref-palette-primary90: #eaddffff;
116
  /* Primary 95 */
117
  --md-ref-palette-primary95: #f6edffff;
118
  /* Primary 99 */
119
  --md-ref-palette-primary99: #fffbfeff;
120
  /* Primary 100 */
121
  --md-ref-palette-primary100: #ffffffff;
122
  /* Neutral Variant 0 */
123
  --md-ref-palette-neutral-variant0: #000000ff;
124
  /* Neutral Variant 10 */
125
  --md-ref-palette-neutral-variant10: #1d1a22ff;
126
  /* Neutral Variant 20 */
127
  --md-ref-palette-neutral-variant20: #322f37ff;
128
  /* Neutral Variant 30 */
129
  --md-ref-palette-neutral-variant30: #49454fff;
130
  /* Neutral Variant 40 */
131
  --md-ref-palette-neutral-variant40: #605d66ff;
132
  /* Neutral Variant 50 */
133
  --md-ref-palette-neutral-variant50: #79747eff;
134
  /* Neutral Variant 60 */
135
  --md-ref-palette-neutral-variant60: #938f99ff;
136
  /* Neutral Variant 70 */
137
  --md-ref-palette-neutral-variant70: #aea9b4ff;
138
  /* Neutral Variant 80 */
139
  --md-ref-palette-neutral-variant80: #cac4d0ff;
140
  /* Neutral Variant 90 */
141
  --md-ref-palette-neutral-variant90: #e7e0ecff;
142
  /* Neutral Variant 95 */
143
  --md-ref-palette-neutral-variant95: #f5eefaff;
144
  /* Neutral Variant 99 */
145
  --md-ref-palette-neutral-variant99: #fffbfeff;
146
  /* Neutral Variant 100 */
147
  --md-ref-palette-neutral-variant100: #ffffffff;
148
  /* Neutral 0 */
149
  --md-ref-palette-neutral0: #000000ff;
150
  /* Neutral 10 */
151
  --md-ref-palette-neutral10: #1c1b1fff;
152
  /* Neutral 20 */
153
  --md-ref-palette-neutral20: #313033ff;
154
  /* Neutral 30 */
155
  --md-ref-palette-neutral30: #484649ff;
156
  /* Neutral 40 */
157
  --md-ref-palette-neutral40: #605d62ff;
158
  /* Neutral 50 */
159
  --md-ref-palette-neutral50: #787579ff;
160
  /* Neutral 60 */
161
  --md-ref-palette-neutral60: #939094ff;
162
  /* Neutral 70 */
163
  --md-ref-palette-neutral70: #aeaaaeff;
164
  /* Neutral 80 */
165
  --md-ref-palette-neutral80: #c9c5caff;
166
  /* Neutral 90 */
167
  --md-ref-palette-neutral90: #e6e1e5ff;
168
  /* Neutral 95 */
169
  --md-ref-palette-neutral95: #f4eff4ff;
170
  /* Neutral 99 */
171
  --md-ref-palette-neutral99: #fffbfeff;
172
  /* Neutral 100 */
173
  --md-ref-palette-neutral100: #ffffffff;
174
  /* Black */
175
  --md-ref-palette-black: #000000ff;
176
  /* White */
177
  --md-ref-palette-white: #ffffffff;
178
}
179

F. public / material-tokens / css / shape.css

1
/*
2
 Copyright 2016 Google Inc. All rights reserved.
3
4
 Licensed under the Apache License, Version 2.0 (the "License");
5
 you may not use this file except in compliance with the License.
6
 You may obtain a copy of the License at
7
8
     http://www.apache.org/licenses/LICENSE-2.0
9
10
 Unless required by applicable law or agreed to in writing, software
11
 distributed under the License is distributed on an "AS IS" BASIS,
12
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 See the License for the specific language governing permissions and
14
 limitations under the License.
15
*/
16
17
:root {
18
  /* Fully rounded */
19
  --md-sys-shape-corner-full-family: 3px;
20
  /* Extra large top rounding */
21
  --md-sys-shape-corner-extra-large-top-family: 1px;
22
  --md-sys-shape-corner-extra-large-top-default-size: 0px;
23
  --md-sys-shape-corner-extra-large-top-top-left: 28px;
24
  --md-sys-shape-corner-extra-large-top-top-right-unit: 1px;
25
  --md-sys-shape-corner-extra-large-top-top-right: 28px;
26
  /* Extra large rounding */
27
  --md-sys-shape-corner-extra-large-family: 1px;
28
  --md-sys-shape-corner-extra-large-default-size-unit: 1px;
29
  --md-sys-shape-corner-extra-large-default-size: 28px;
30
  /* Large top rounding */
31
  --md-sys-shape-corner-large-top-family: 1px;
32
  --md-sys-shape-corner-large-top-default-size-unit: 1px;
33
  --md-sys-shape-corner-large-top-default-size: 0px;
34
  --md-sys-shape-corner-large-top-top-left-unit: 1px;
35
  --md-sys-shape-corner-large-top-top-left: 16px;
36
  --md-sys-shape-corner-large-top-top-right-unit: 1px;
37
  --md-sys-shape-corner-large-top-top-right: 16px;
38
  /* Large end rounding */
39
  --md-sys-shape-corner-large-end-family: 1px;
40
  --md-sys-shape-corner-large-end-default-size-unit: 1px;
41
  --md-sys-shape-corner-large-end-default-size: 0px;
42
  --md-sys-shape-corner-large-end-top-right-unit: 1px;
43
  --md-sys-shape-corner-large-end-top-right: 16px;
44
  --md-sys-shape-corner-large-end-bottom-right-unit: 1px;
45
  --md-sys-shape-corner-large-end-bottom-right: 16px;
46
  /* Large rounding */
47
  --md-sys-shape-corner-large-family: 1px;
48
  --md-sys-shape-corner-large-default-size-unit: 1px;
49
  --md-sys-shape-corner-large-default-size: 16px;
50
  /* Medium rounding */
51
  --md-sys-shape-corner-medium-family: 1px;
52
  --md-sys-shape-corner-medium-default-size-unit: 1px;
53
  --md-sys-shape-corner-medium-default-size: 12px;
54
  /* Small rounding */
55
  --md-sys-shape-corner-small-family: 1px;
56
  --md-sys-shape-corner-small-default-size-unit: 1px;
57
  --md-sys-shape-corner-small-default-size: 8px;
58
  /* Extra small top rounding */
59
  --md-sys-shape-corner-extra-small-top-family: 1px;
60
  --md-sys-shape-corner-extra-small-top-default-size-unit: 1px;
61
  --md-sys-shape-corner-extra-small-top-default-size: 0px;
62
  --md-sys-shape-corner-extra-small-top-top-left-unit: 1px;
63
  --md-sys-shape-corner-extra-small-top-top-left: 4px;
64
  --md-sys-shape-corner-extra-small-top-top-right-unit: 1px;
65
  --md-sys-shape-corner-extra-small-top-top-right: 4px;
66
  /* Extra small rounding */
67
  --md-sys-shape-corner-extra-small-family: 1px;
68
  --md-sys-shape-corner-extra-small-default-size-unit: 1px;
69
  --md-sys-shape-corner-extra-small-default-size: 4px;
70
  /* No rounding */
71
  --md-sys-shape-corner-none-family: 1px;
72
  --md-sys-shape-corner-none-default-size-unit: 1px;
73
  --md-sys-shape-corner-none-default-size: 0px;
74
75
  --md-sys-shape-small: var(--md-sys-shape-corner-small-default-size);
76
  --md-sys-shape-medium: var(--md-sys-shape-corner-medium-default-size);
77
  --md-sys-shape-large: var(--md-sys-shape-corner-large-default-size);
78
}
79
80
.shape-none {
81
  border-radius: var(--md-sys-shape-corner-none-default-size);
82
}
83
.shape-extra-small {
84
  border-radius: var(--md-sys-shape-corner-extra-small-default-size);
85
}
86
.shape-small {
87
  border-radius: var(--md-sys-shape-corner-small-default-size);
88
}
89
.shape-medium {
90
  border-radius: var(--md-sys-shape-corner-medium-default-size);
91
}
92
.shape-large {
93
  border-radius: var(--md-sys-shape-corner-large-default-size);
94
}
95
.shape-extra-large {
96
  border-radius: var(--md-sys-shape-corner-extra-large-default-size);
97
}
98
.extra-small-top {
99
  border-top-left-radius: var(--md-sys-shape-corner-extra-small-top-top-left);
100
  border-top-right-radius: var(--md-sys-shape-corner-extra-small-top-top-right);
101
}
102
.large-end {
103
  border-top-right-radius: var(--md-sys-shape-corner-large-end-top-right);
104
  border-bottom-right-radius: var(--md-sys-shape-corner-large-end-bottom-right);
105
}
106
.large-top {
107
  border-top-left-radius: var(--md-sys-shape-corner-large-top-top-left);
108
  border-top-right-radius: var(--md-sys-shape-corner-large-top-top-right);
109
}
110
.extra-large-top {
111
  border-top-left-radius: var(--md-sys-shape-corner-extra-large-top-top-left);
112
  border-top-right-radius: var(--md-sys-shape-corner-extra-large-top-top-right);
113
}
114

G. public / material-tokens / css / state.css

1
/*
2
 Copyright 2016 Google Inc. All rights reserved.
3
4
 Licensed under the Apache License, Version 2.0 (the "License");
5
 you may not use this file except in compliance with the License.
6
 You may obtain a copy of the License at
7
8
     http://www.apache.org/licenses/LICENSE-2.0
9
10
 Unless required by applicable law or agreed to in writing, software
11
 distributed under the License is distributed on an "AS IS" BASIS,
12
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 See the License for the specific language governing permissions and
14
 limitations under the License.
15
*/
16
17
:root {
18
  /* Dragged state layer opacity */
19
  --md-sys-state-dragged-state-layer-opacity: 0.1599999964237213;
20
  /* Pressed state layer opacity */
21
  --md-sys-state-pressed-state-layer-opacity: 0.11999999731779099;
22
  /* Focus state layer opacity */
23
  --md-sys-state-focus-state-layer-opacity: 0.11999999731779099;
24
  /* Hover state layer opacity */
25
  --md-sys-state-hover-state-layer-opacity: 0.07999999821186066;
26
}
27
.hover-state-layer {
28
  opacity: var(--md-sys-state-hover-state-layer-opacity);
29
}
30
.pressed-state-layer {
31
  opacity: var(--md-sys-state-pressed-state-layer-opacity);
32
}
33
.dragged-state-layer {
34
  opacity: var(--md-sys-state-dragged-state-layer-opacity);
35
}
36
.focus-state-layer {
37
  opacity: var(--md-sys-state-focus-state-layer-opacity);
38
}
39

H. public / material-tokens / css / typography.css

1
/*
2
 Copyright 2016 Google Inc. All rights reserved.
3
4
 Licensed under the Apache License, Version 2.0 (the "License");
5
 you may not use this file except in compliance with the License.
6
 You may obtain a copy of the License at
7
8
     http://www.apache.org/licenses/LICENSE-2.0
9
10
 Unless required by applicable law or agreed to in writing, software
11
 distributed under the License is distributed on an "AS IS" BASIS,
12
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 See the License for the specific language governing permissions and
14
 limitations under the License.
15
*/
16
17
/* This file is generated */
18
19
/* DO NOT EDIT */
20
21
:root {
22
  /* Label Small */
23
  --md-sys-typescale-label-small-text-transform: unset;
24
  --md-sys-typescale-label-small-axis-value: unset;
25
  --md-sys-typescale-label-small-font-style: unset;
26
  --md-sys-typescale-label-small-text-decoration: unset;
27
  /* Label Small line height */
28
  --md-sys-typescale-label-small-line-height-value: 16px;
29
  --md-sys-typescale-label-small-line-height-unit: 2px;
30
  --md-sys-typescale-label-small-line-height: 16px;
31
  /* Label Small font tracking */
32
  --md-sys-typescale-label-small-tracking-value: 0.5px;
33
  --md-sys-typescale-label-small-tracking-unit: 2px;
34
  --md-sys-typescale-label-small-tracking: 0.5px;
35
  /* Label Small font size */
36
  --md-sys-typescale-label-small-size-value: 11px;
37
  --md-sys-typescale-label-small-size-unit: 2px;
38
  --md-sys-typescale-label-small-size: 11px;
39
  /* Label Small font weight */
40
  --md-sys-typescale-label-small-weight: var(--md-ref-typeface-weight-medium);
41
  /* Label Small font name */
42
  --md-sys-typescale-label-small-font: var(--md-ref-typeface-plain);
43
  /* Label Medium */
44
  --md-sys-typescale-label-medium-axis-value: unset;
45
  --md-sys-typescale-label-medium-font-style: unset;
46
  --md-sys-typescale-label-medium-text-decoration: unset;
47
  /* Label Medium text transform */
48
  --md-sys-typescale-label-medium-text-transform: 1;
49
  /* Label Medium line height */
50
  --md-sys-typescale-label-medium-line-height-value: 16px;
51
  --md-sys-typescale-label-medium-line-height-unit: 2px;
52
  --md-sys-typescale-label-medium-line-height: 16px;
53
  /* Label Medium font tracking */
54
  --md-sys-typescale-label-medium-tracking-value: 0.5px;
55
  --md-sys-typescale-label-medium-tracking-unit: 2px;
56
  --md-sys-typescale-label-medium-tracking: 0.5px;
57
  /* Label Medium font size */
58
  --md-sys-typescale-label-medium-size-value: 12px;
59
  --md-sys-typescale-label-medium-size-unit: 2px;
60
  --md-sys-typescale-label-medium-size: 12px;
61
  /* Label Medium font weight */
62
  --md-sys-typescale-label-medium-weight: var(--md-ref-typeface-weight-medium);
63
  /* Label Medium font name */
64
  --md-sys-typescale-label-medium-font: var(--md-ref-typeface-plain);
65
  /* Label Large */
66
  --md-sys-typescale-label-large-text-transform: unset;
67
  --md-sys-typescale-label-large-axis-value: unset;
68
  --md-sys-typescale-label-large-font-style: unset;
69
  --md-sys-typescale-label-large-text-decoration: unset;
70
  /* Label Large line height */
71
  --md-sys-typescale-label-large-line-height-value: 20px;
72
  --md-sys-typescale-label-large-line-height-unit: 2px;
73
  --md-sys-typescale-label-large-line-height: 20px;
74
  /* Label Large font tracking */
75
  --md-sys-typescale-label-large-tracking-value: 0.10000000149011612px;
76
  --md-sys-typescale-label-large-tracking-unit: 2px;
77
  --md-sys-typescale-label-large-tracking: 0.10000000149011612px;
78
  /* Label Large font size */
79
  --md-sys-typescale-label-large-size-value: 14px;
80
  --md-sys-typescale-label-large-size-unit: 2px;
81
  --md-sys-typescale-label-large-size: 14px;
82
  /* Label Large font weight */
83
  --md-sys-typescale-label-large-weight: var(--md-ref-typeface-weight-medium);
84
  /* Label Large font name */
85
  --md-sys-typescale-label-large-font: var(--md-ref-typeface-plain);
86
  /* Body Small */
87
  --md-sys-typescale-body-small-text-transform: unset;
88
  --md-sys-typescale-body-small-axis-value: unset;
89
  --md-sys-typescale-body-small-font-style: unset;
90
  --md-sys-typescale-body-small-text-decoration: unset;
91
  /* Body Small line height */
92
  --md-sys-typescale-body-small-line-height-value: 16px;
93
  --md-sys-typescale-body-small-line-height-unit: 2px;
94
  --md-sys-typescale-body-small-line-height: 16px;
95
  /* Body Small font tracking */
96
  --md-sys-typescale-body-small-tracking-value: 0.4000000059604645px;
97
  --md-sys-typescale-body-small-tracking-unit: 2px;
98
  --md-sys-typescale-body-small-tracking: 0.4000000059604645px;
99
  /* Body Small font size */
100
  --md-sys-typescale-body-small-size-value: 12px;
101
  --md-sys-typescale-body-small-size-unit: 2px;
102
  --md-sys-typescale-body-small-size: 12px;
103
  /* Body Small font weight */
104
  --md-sys-typescale-body-small-weight: var(--md-ref-typeface-weight-regular);
105
  /* Body Small font name */
106
  --md-sys-typescale-body-small-font: var(--md-ref-typeface-plain);
107
  /* Body Medium */
108
  --md-sys-typescale-body-medium-text-transform: unset;
109
  --md-sys-typescale-body-medium-axis-value: unset;
110
  --md-sys-typescale-body-medium-font-style: unset;
111
  --md-sys-typescale-body-medium-text-decoration: unset;
112
  /* Body Medium line height */
113
  --md-sys-typescale-body-medium-line-height-value: 20px;
114
  --md-sys-typescale-body-medium-line-height-unit: 2px;
115
  --md-sys-typescale-body-medium-line-height: 20px;
116
  /* Body Medium font tracking */
117
  --md-sys-typescale-body-medium-tracking-value: 0.25px;
118
  --md-sys-typescale-body-medium-tracking-unit: 2px;
119
  --md-sys-typescale-body-medium-tracking: 0.25px;
120
  /* Body Medium font size */
121
  --md-sys-typescale-body-medium-size-value: 14px;
122
  --md-sys-typescale-body-medium-size-unit: 2px;
123
  --md-sys-typescale-body-medium-size: 14px;
124
  /* Body Medium font weight */
125
  --md-sys-typescale-body-medium-weight: var(--md-ref-typeface-weight-regular);
126
  /* Body Medium font name */
127
  --md-sys-typescale-body-medium-font: var(--md-ref-typeface-plain);
128
  /* Body Large */
129
  --md-sys-typescale-body-large-text-transform: unset;
130
  --md-sys-typescale-body-large-axis-value: unset;
131
  --md-sys-typescale-body-large-font-style: unset;
132
  --md-sys-typescale-body-large-text-decoration: unset;
133
  /* Body Large line height */
134
  --md-sys-typescale-body-large-line-height-value: 24px;
135
  --md-sys-typescale-body-large-line-height-unit: 2px;
136
  --md-sys-typescale-body-large-line-height: 24px;
137
  /* Body Large font tracking */
138
  --md-sys-typescale-body-large-tracking-value: 0.5px;
139
  --md-sys-typescale-body-large-tracking-unit: 2px;
140
  --md-sys-typescale-body-large-tracking: 0.5px;
141
  /* Body Large font size */
142
  --md-sys-typescale-body-large-size-value: 16px;
143
  --md-sys-typescale-body-large-size-unit: 2px;
144
  --md-sys-typescale-body-large-size: 16px;
145
  /* Body Large font weight */
146
  --md-sys-typescale-body-large-weight: var(--md-ref-typeface-weight-regular);
147
  /* Body Large font name */
148
  --md-sys-typescale-body-large-font: var(--md-ref-typeface-plain);
149
  /* Title Small */
150
  --md-sys-typescale-title-small-text-transform: unset;
151
  --md-sys-typescale-title-small-axis-value: unset;
152
  --md-sys-typescale-title-small-font-style: unset;
153
  --md-sys-typescale-title-small-text-decoration: unset;
154
  /* Title Small line height */
155
  --md-sys-typescale-title-small-line-height-value: 20px;
156
  --md-sys-typescale-title-small-line-height-unit: 2px;
157
  --md-sys-typescale-title-small-line-height: 20px;
158
  /* Title Small font tracking */
159
  --md-sys-typescale-title-small-tracking-value: 0.10000000149011612px;
160
  --md-sys-typescale-title-small-tracking-unit: 2px;
161
  --md-sys-typescale-title-small-tracking: 0.10000000149011612px;
162
  /* Title Small font size */
163
  --md-sys-typescale-title-small-size-value: 14px;
164
  --md-sys-typescale-title-small-size-unit: 2px;
165
  --md-sys-typescale-title-small-size: 14px;
166
  /* Title Small font weight */
167
  --md-sys-typescale-title-small-weight: var(--md-ref-typeface-weight-medium);
168
  /* Title Small font name */
169
  --md-sys-typescale-title-small-font: var(--md-ref-typeface-plain);
170
  /* Title Medium */
171
  --md-sys-typescale-title-medium-text-transform: unset;
172
  --md-sys-typescale-title-medium-axis-value: unset;
173
  --md-sys-typescale-title-medium-font-style: unset;
174
  --md-sys-typescale-title-medium-text-decoration: unset;
175
  /* Title Medium line height */
176
  --md-sys-typescale-title-medium-line-height-value: 24px;
177
  --md-sys-typescale-title-medium-line-height-unit: 2px;
178
  --md-sys-typescale-title-medium-line-height: 24px;
179
  /* Title Medium font tracking */
180
  --md-sys-typescale-title-medium-tracking-value: 0.15000000596046448px;
181
  --md-sys-typescale-title-medium-tracking-unit: 2px;
182
  --md-sys-typescale-title-medium-tracking: 0.15000000596046448px;
183
  /* Title Medium font size */
184
  --md-sys-typescale-title-medium-size-value: 16px;
185
  --md-sys-typescale-title-medium-size-unit: 2px;
186
  --md-sys-typescale-title-medium-size: 16px;
187
  /* Title Medium font weight */
188
  --md-sys-typescale-title-medium-weight: var(--md-ref-typeface-weight-medium);
189
  /* Title Medium font name */
190
  --md-sys-typescale-title-medium-font: var(--md-ref-typeface-plain);
191
  /* Title Large */
192
  --md-sys-typescale-title-large-text-transform: unset;
193
  --md-sys-typescale-title-large-axis-value: unset;
194
  --md-sys-typescale-title-large-font-style: unset;
195
  --md-sys-typescale-title-large-text-decoration: unset;
196
  /* Title Large line height */
197
  --md-sys-typescale-title-large-line-height-value: 28px;
198
  --md-sys-typescale-title-large-line-height-unit: 2px;
199
  --md-sys-typescale-title-large-line-height: 28px;
200
  /* Title Large font tracking */
201
  --md-sys-typescale-title-large-tracking-value: 0px;
202
  --md-sys-typescale-title-large-tracking-unit: 2px;
203
  --md-sys-typescale-title-large-tracking: 0px;
204
  /* Title Large font size */
205
  --md-sys-typescale-title-large-size-value: 22px;
206
  --md-sys-typescale-title-large-size-unit: 2px;
207
  --md-sys-typescale-title-large-size: 22px;
208
  /* Title Large font weight */
209
  --md-sys-typescale-title-large-weight: var(--md-ref-typeface-weight-regular);
210
  /* Title Large font name */
211
  --md-sys-typescale-title-large-font: var(--md-ref-typeface-brand);
212
  /* Headline Small */
213
  --md-sys-typescale-headline-small-text-transform: unset;
214
  --md-sys-typescale-headline-small-axis-value: unset;
215
  --md-sys-typescale-headline-small-font-style: unset;
216
  --md-sys-typescale-headline-small-text-decoration: unset;
217
  /* Headline Small line height */
218
  --md-sys-typescale-headline-small-line-height-value: 32px;
219
  --md-sys-typescale-headline-small-line-height-unit: 2px;
220
  --md-sys-typescale-headline-small-line-height: 32px;
221
  /* Headline Small font tracking */
222
  --md-sys-typescale-headline-small-tracking-value: 0px;
223
  --md-sys-typescale-headline-small-tracking-unit: 2px;
224
  --md-sys-typescale-headline-small-tracking: 0px;
225
  /* Headline Small font size */
226
  --md-sys-typescale-headline-small-size-value: 24px;
227
  --md-sys-typescale-headline-small-size-unit: 2px;
228
  --md-sys-typescale-headline-small-size: 24px;
229
  /* Headline Small font weight */
230
  --md-sys-typescale-headline-small-weight: var(
231
    --md-ref-typeface-weight-regular
232
  );
233
  /* Headline Small font name */
234
  --md-sys-typescale-headline-small-font: var(--md-ref-typeface-brand);
235
  /* Headline Medium */
236
  --md-sys-typescale-headline-medium-text-transform: unset;
237
  --md-sys-typescale-headline-medium-axis-value: unset;
238
  --md-sys-typescale-headline-medium-font-style: unset;
239
  --md-sys-typescale-headline-medium-text-decoration: unset;
240
  /* Headline Medium line height */
241
  --md-sys-typescale-headline-medium-line-height-value: 36px;
242
  --md-sys-typescale-headline-medium-line-height-unit: 2px;
243
  --md-sys-typescale-headline-medium-line-height: 36px;
244
  /* Headline Medium font tracking */
245
  --md-sys-typescale-headline-medium-tracking-value: 0px;
246
  --md-sys-typescale-headline-medium-tracking-unit: 2px;
247
  --md-sys-typescale-headline-medium-tracking: 0px;
248
  /* Headline Medium font size */
249
  --md-sys-typescale-headline-medium-size-value: 28px;
250
  --md-sys-typescale-headline-medium-size-unit: 2px;
251
  --md-sys-typescale-headline-medium-size: 28px;
252
  /* Headline Medium font weight */
253
  --md-sys-typescale-headline-medium-weight: var(
254
    --md-ref-typeface-weight-regular
255
  );
256
  /* Headline Medium font name */
257
  --md-sys-typescale-headline-medium-font: var(--md-ref-typeface-brand);
258
  /* Headline Large */
259
  --md-sys-typescale-headline-large-text-transform: unset;
260
  --md-sys-typescale-headline-large-axis-value: unset;
261
  --md-sys-typescale-headline-large-font-style: unset;
262
  --md-sys-typescale-headline-large-text-decoration: unset;
263
  /* Headline Large line height */
264
  --md-sys-typescale-headline-large-line-height-value: 40px;
265
  --md-sys-typescale-headline-large-line-height-unit: 2px;
266
  --md-sys-typescale-headline-large-line-height: 40px;
267
  /* Headline Large font tracking */
268
  --md-sys-typescale-headline-large-tracking-value: 0px;
269
  --md-sys-typescale-headline-large-tracking-unit: 2px;
270
  --md-sys-typescale-headline-large-tracking: 0px;
271
  /* Headline Large font size */
272
  --md-sys-typescale-headline-large-size-value: 32px;
273
  --md-sys-typescale-headline-large-size-unit: 2px;
274
  --md-sys-typescale-headline-large-size: 32px;
275
  /* Headline Large font name */
276
  --md-sys-typescale-headline-large-font: var(--md-ref-typeface-brand);
277
  /* Headline Large font weight */
278
  --md-sys-typescale-headline-large-weight: var(
279
    --md-ref-typeface-weight-regular
280
  );
281
  /* Display Small */
282
  --md-sys-typescale-display-small-text-transform: unset;
283
  --md-sys-typescale-display-small-axis-value: unset;
284
  --md-sys-typescale-display-small-font-style: unset;
285
  --md-sys-typescale-display-small-text-decoration: unset;
286
  /* Display Small line height */
287
  --md-sys-typescale-display-small-line-height-value: 44px;
288
  --md-sys-typescale-display-small-line-height-unit: 2px;
289
  --md-sys-typescale-display-small-line-height: 44px;
290
  /* Display Small font tracking */
291
  --md-sys-typescale-display-small-tracking-value: 0px;
292
  --md-sys-typescale-display-small-tracking-unit: 2px;
293
  --md-sys-typescale-display-small-tracking: 0px;
294
  /* Display Small font size */
295
  --md-sys-typescale-display-small-size-value: 36px;
296
  --md-sys-typescale-display-small-size-unit: 2px;
297
  --md-sys-typescale-display-small-size: 36px;
298
  /* Display Small font weight */
299
  --md-sys-typescale-display-small-weight: var(
300
    --md-ref-typeface-weight-regular
301
  );
302
  /* Display Small font name */
303
  --md-sys-typescale-display-small-font: var(--md-ref-typeface-brand);
304
  /* Display Medium */
305
  --md-sys-typescale-display-medium-text-transform: unset;
306
  --md-sys-typescale-display-medium-axis-value: unset;
307
  --md-sys-typescale-display-medium-font-style: unset;
308
  --md-sys-typescale-display-medium-text-decoration: unset;
309
  /* Display Medium line height */
310
  --md-sys-typescale-display-medium-line-height-value: 52px;
311
  --md-sys-typescale-display-medium-line-height-unit: 2px;
312
  --md-sys-typescale-display-medium-line-height: 52px;
313
  /* Display Medium font tracking */
314
  --md-sys-typescale-display-medium-tracking-value: 0px;
315
  --md-sys-typescale-display-medium-tracking-unit: 2px;
316
  --md-sys-typescale-display-medium-tracking: 0px;
317
  /* Display Medium font size */
318
  --md-sys-typescale-display-medium-size-value: 45px;
319
  --md-sys-typescale-display-medium-size-unit: 2px;
320
  --md-sys-typescale-display-medium-size: 45px;
321
  /* Display Medium font weight */
322
  --md-sys-typescale-display-medium-weight: var(
323
    --md-ref-typeface-weight-regular
324
  );
325
  /* Display Medium font name */
326
  --md-sys-typescale-display-medium-font: var(--md-ref-typeface-brand);
327
  /* Display Large */
328
  --md-sys-typescale-display-large-text-transform: unset;
329
  --md-sys-typescale-display-large-axis-value: unset;
330
  --md-sys-typescale-display-large-font-style: unset;
331
  --md-sys-typescale-display-large-text-decoration: unset;
332
  /* Display Large line height */
333
  --md-sys-typescale-display-large-line-height-value: 64px;
334
  --md-sys-typescale-display-large-line-height-unit: 2px;
335
  --md-sys-typescale-display-large-line-height: 64px;
336
  /* Display Large font tracking */
337
  --md-sys-typescale-display-large-tracking-value: -0.25px;
338
  --md-sys-typescale-display-large-tracking-unit: 2px;
339
  --md-sys-typescale-display-large-tracking: -0.25px;
340
  /* Display Large font size */
341
  --md-sys-typescale-display-large-size-value: 57px;
342
  --md-sys-typescale-display-large-size-unit: 2px;
343
  --md-sys-typescale-display-large-size: 57px;
344
  /* Display Large font weight */
345
  --md-sys-typescale-display-large-weight: var(
346
    --md-ref-typeface-weight-regular
347
  );
348
  /* Display Large font name */
349
  --md-sys-typescale-display-large-font: var(--md-ref-typeface-brand);
350
  /* Plain typeface */
351
  --md-ref-typeface-plain: Roboto;
352
  /* Brand typeface */
353
  --md-ref-typeface-brand: Roboto;
354
  /* Bold weight */
355
  --md-ref-typeface-weight-bold: 700;
356
  /* Medium weight */
357
  --md-ref-typeface-weight-medium: 500;
358
  /* Regular weight */
359
  --md-ref-typeface-weight-regular: 400;
360
}
361
362
/* Label Small */
363
.label-small {
364
  font-family: var(--md-sys-typescale-label-small-font);
365
  font-weight: var(--md-sys-typescale-label-small-weight);
366
  font-size: var(--md-sys-typescale-label-small-size);
367
  font-style: var(--md-sys-typescale-label-small-font-style);
368
  letter-spacing: var(--md-sys-typescale-label-small-tracking);
369
  line-height: var(--md-sys-typescale-label-small-line-height);
370
  text-transform: var(--md-sys-typescale-label-small-text-transform);
371
  text-decoration: var(--md-sys-typescale-label-small-text-decoration);
372
}
373
/* Label Medium */
374
.label-medium {
375
  font-family: var(--md-sys-typescale-label-medium-font);
376
  font-weight: var(--md-sys-typescale-label-medium-weight);
377
  font-size: var(--md-sys-typescale-label-medium-size);
378
  font-style: var(--md-sys-typescale-label-medium-font-style);
379
  letter-spacing: var(--md-sys-typescale-label-medium-tracking);
380
  line-height: var(--md-sys-typescale-label-medium-line-height);
381
  text-transform: var(--md-sys-typescale-label-medium-text-transform);
382
  text-decoration: var(--md-sys-typescale-label-medium-text-decoration);
383
}
384
/* Label Large */
385
.label-large {
386
  font-family: var(--md-sys-typescale-label-large-font);
387
  font-weight: var(--md-sys-typescale-label-large-weight);
388
  font-size: var(--md-sys-typescale-label-large-size);
389
  font-style: var(--md-sys-typescale-label-large-font-style);
390
  letter-spacing: var(--md-sys-typescale-label-large-tracking);
391
  line-height: var(--md-sys-typescale-label-large-line-height);
392
  text-transform: var(--md-sys-typescale-label-large-text-transform);
393
  text-decoration: var(--md-sys-typescale-label-large-text-decoration);
394
}
395
/* Body Small */
396
.body-small {
397
  font-family: var(--md-sys-typescale-body-small-font);
398
  font-weight: var(--md-sys-typescale-body-small-weight);
399
  font-size: var(--md-sys-typescale-body-small-size);
400
  font-style: var(--md-sys-typescale-body-small-font-style);
401
  letter-spacing: var(--md-sys-typescale-body-small-tracking);
402
  line-height: var(--md-sys-typescale-body-small-line-height);
403
  text-transform: var(--md-sys-typescale-body-small-text-transform);
404
  text-decoration: var(--md-sys-typescale-body-small-text-decoration);
405
}
406
/* Body Medium */
407
.body-medium {
408
  font-family: var(--md-sys-typescale-body-medium-font);
409
  font-weight: var(--md-sys-typescale-body-medium-weight);
410
  font-size: var(--md-sys-typescale-body-medium-size);
411
  font-style: var(--md-sys-typescale-body-medium-font-style);
412
  letter-spacing: var(--md-sys-typescale-body-medium-tracking);
413
  line-height: var(--md-sys-typescale-body-medium-line-height);
414
  text-transform: var(--md-sys-typescale-body-medium-text-transform);
415
  text-decoration: var(--md-sys-typescale-body-medium-text-decoration);
416
}
417
/* Body Large */
418
.body-large {
419
  font-family: var(--md-sys-typescale-body-large-font);
420
  font-weight: var(--md-sys-typescale-body-large-weight);
421
  font-size: var(--md-sys-typescale-body-large-size);
422
  font-style: var(--md-sys-typescale-body-large-font-style);
423
  letter-spacing: var(--md-sys-typescale-body-large-tracking);
424
  line-height: var(--md-sys-typescale-body-large-line-height);
425
  text-transform: var(--md-sys-typescale-body-large-text-transform);
426
  text-decoration: var(--md-sys-typescale-body-large-text-decoration);
427
}
428
/* Title Small */
429
.title-small {
430
  font-family: var(--md-sys-typescale-title-small-font);
431
  font-weight: var(--md-sys-typescale-title-small-weight);
432
  font-size: var(--md-sys-typescale-title-small-size);
433
  font-style: var(--md-sys-typescale-title-small-font-style);
434
  letter-spacing: var(--md-sys-typescale-title-small-tracking);
435
  line-height: var(--md-sys-typescale-title-small-line-height);
436
  text-transform: var(--md-sys-typescale-title-small-text-transform);
437
  text-decoration: var(--md-sys-typescale-title-small-text-decoration);
438
}
439
/* Title Medium */
440
.title-medium {
441
  font-family: var(--md-sys-typescale-title-medium-font);
442
  font-weight: var(--md-sys-typescale-title-medium-weight);
443
  font-size: var(--md-sys-typescale-title-medium-size);
444
  font-style: var(--md-sys-typescale-title-medium-font-style);
445
  letter-spacing: var(--md-sys-typescale-title-medium-tracking);
446
  line-height: var(--md-sys-typescale-title-medium-line-height);
447
  text-transform: var(--md-sys-typescale-title-medium-text-transform);
448
  text-decoration: var(--md-sys-typescale-title-medium-text-decoration);
449
}
450
/* Title Large */
451
.title-large {
452
  font-family: var(--md-sys-typescale-title-large-font);
453
  font-weight: var(--md-sys-typescale-title-large-weight);
454
  font-size: var(--md-sys-typescale-title-large-size);
455
  font-style: var(--md-sys-typescale-title-large-font-style);
456
  letter-spacing: var(--md-sys-typescale-title-large-tracking);
457
  line-height: var(--md-sys-typescale-title-large-line-height);
458
  text-transform: var(--md-sys-typescale-title-large-text-transform);
459
  text-decoration: var(--md-sys-typescale-title-large-text-decoration);
460
}
461
/* Headline Small */
462
.headline-small {
463
  font-family: var(--md-sys-typescale-headline-small-font);
464
  font-weight: var(--md-sys-typescale-headline-small-weight);
465
  font-size: var(--md-sys-typescale-headline-small-size);
466
  font-style: var(--md-sys-typescale-headline-small-font-style);
467
  letter-spacing: var(--md-sys-typescale-headline-small-tracking);
468
  line-height: var(--md-sys-typescale-headline-small-line-height);
469
  text-transform: var(--md-sys-typescale-headline-small-text-transform);
470
  text-decoration: var(--md-sys-typescale-headline-small-text-decoration);
471
}
472
/* Headline Medium */
473
.headline-medium {
474
  font-family: var(--md-sys-typescale-headline-medium-font);
475
  font-weight: var(--md-sys-typescale-headline-medium-weight);
476
  font-size: var(--md-sys-typescale-headline-medium-size);
477
  font-style: var(--md-sys-typescale-headline-medium-font-style);
478
  letter-spacing: var(--md-sys-typescale-headline-medium-tracking);
479
  line-height: var(--md-sys-typescale-headline-medium-line-height);
480
  text-transform: var(--md-sys-typescale-headline-medium-text-transform);
481
  text-decoration: var(--md-sys-typescale-headline-medium-text-decoration);
482
}
483
/* Headline Large */
484
.headline-large {
485
  font-family: var(--md-sys-typescale-headline-large-font);
486
  font-weight: var(--md-sys-typescale-headline-large-weight);
487
  font-size: var(--md-sys-typescale-headline-large-size);
488
  font-style: var(--md-sys-typescale-headline-large-font-style);
489
  letter-spacing: var(--md-sys-typescale-headline-large-tracking);
490
  line-height: var(--md-sys-typescale-headline-large-line-height);
491
  text-transform: var(--md-sys-typescale-headline-large-text-transform);
492
  text-decoration: var(--md-sys-typescale-headline-large-text-decoration);
493
}
494
/* Display Small */
495
.display-small {
496
  font-family: var(--md-sys-typescale-display-small-font);
497
  font-weight: var(--md-sys-typescale-display-small-weight);
498
  font-size: var(--md-sys-typescale-display-small-size);
499
  font-style: var(--md-sys-typescale-display-small-font-style);
500
  letter-spacing: var(--md-sys-typescale-display-small-tracking);
501
  line-height: var(--md-sys-typescale-display-small-line-height);
502
  text-transform: var(--md-sys-typescale-display-small-text-transform);
503
  text-decoration: var(--md-sys-typescale-display-small-text-decoration);
504
}
505
/* Display Medium */
506
.display-medium {
507
  font-family: var(--md-sys-typescale-display-medium-font);
508
  font-weight: var(--md-sys-typescale-display-medium-weight);
509
  font-size: var(--md-sys-typescale-display-medium-size);
510
  font-style: var(--md-sys-typescale-display-medium-font-style);
511
  letter-spacing: var(--md-sys-typescale-display-medium-tracking);
512
  line-height: var(--md-sys-typescale-display-medium-line-height);
513
  text-transform: var(--md-sys-typescale-display-medium-text-transform);
514
  text-decoration: var(--md-sys-typescale-display-medium-text-decoration);
515
}
516
/* Display Large */
517
.display-large {
518
  font-family: var(--md-sys-typescale-display-large-font);
519
  font-weight: var(--md-sys-typescale-display-large-weight);
520
  font-size: var(--md-sys-typescale-display-large-size);
521
  font-style: var(--md-sys-typescale-display-large-font-style);
522
  letter-spacing: var(--md-sys-typescale-display-large-tracking);
523
  line-height: var(--md-sys-typescale-display-large-line-height);
524
  text-transform: var(--md-sys-typescale-display-large-text-transform);
525
  text-decoration: var(--md-sys-typescale-display-large-text-decoration);
526
}
527

I. Carpeta « public / material-tokens / css / theme »

1. public / material-tokens / css / theme / dark.css

1
.dark {
2
  --md-sys-color-primary: rgb(170 199 255);
3
  --md-sys-color-surface-tint: rgb(170 199 255);
4
  --md-sys-color-on-primary: rgb(10 48 95);
5
  --md-sys-color-primary-container: rgb(40 71 119);
6
  --md-sys-color-on-primary-container: rgb(214 227 255);
7
  --md-sys-color-secondary: rgb(190 198 220);
8
  --md-sys-color-on-secondary: rgb(40 49 65);
9
  --md-sys-color-secondary-container: rgb(62 71 89);
10
  --md-sys-color-on-secondary-container: rgb(218 226 249);
11
  --md-sys-color-tertiary: rgb(221 188 224);
12
  --md-sys-color-on-tertiary: rgb(63 40 68);
13
  --md-sys-color-tertiary-container: rgb(87 62 92);
14
  --md-sys-color-on-tertiary-container: rgb(250 216 253);
15
  --md-sys-color-error: rgb(255 180 171);
16
  --md-sys-color-on-error: rgb(105 0 5);
17
  --md-sys-color-error-container: rgb(147 0 10);
18
  --md-sys-color-on-error-container: rgb(255 218 214);
19
  --md-sys-color-background: rgb(17 19 24);
20
  --md-sys-color-on-background: rgb(226 226 233);
21
  --md-sys-color-surface: rgb(17 19 24);
22
  --md-sys-color-on-surface: rgb(226 226 233);
23
  --md-sys-color-surface-variant: rgb(68 71 78);
24
  --md-sys-color-on-surface-variant: rgb(196 198 208);
25
  --md-sys-color-outline: rgb(142 144 153);
26
  --md-sys-color-outline-variant: rgb(68 71 78);
27
  --md-sys-color-shadow: rgb(0 0 0);
28
  --md-sys-color-scrim: rgb(0 0 0);
29
  --md-sys-color-inverse-surface: rgb(226 226 233);
30
  --md-sys-color-inverse-on-surface: rgb(46 48 54);
31
  --md-sys-color-inverse-primary: rgb(65 95 145);
32
  --md-sys-color-primary-fixed: rgb(214 227 255);
33
  --md-sys-color-on-primary-fixed: rgb(0 27 62);
34
  --md-sys-color-primary-fixed-dim: rgb(170 199 255);
35
  --md-sys-color-on-primary-fixed-variant: rgb(40 71 119);
36
  --md-sys-color-secondary-fixed: rgb(218 226 249);
37
  --md-sys-color-on-secondary-fixed: rgb(19 28 43);
38
  --md-sys-color-secondary-fixed-dim: rgb(190 198 220);
39
  --md-sys-color-on-secondary-fixed-variant: rgb(62 71 89);
40
  --md-sys-color-tertiary-fixed: rgb(250 216 253);
41
  --md-sys-color-on-tertiary-fixed: rgb(40 19 46);
42
  --md-sys-color-tertiary-fixed-dim: rgb(221 188 224);
43
  --md-sys-color-on-tertiary-fixed-variant: rgb(87 62 92);
44
  --md-sys-color-surface-dim: rgb(17 19 24);
45
  --md-sys-color-surface-bright: rgb(55 57 62);
46
  --md-sys-color-surface-container-lowest: rgb(12 14 19);
47
  --md-sys-color-surface-container-low: rgb(25 28 32);
48
  --md-sys-color-surface-container: rgb(29 32 36);
49
  --md-sys-color-surface-container-high: rgb(40 42 47);
50
  --md-sys-color-surface-container-highest: rgb(51 53 58);
51
}
52

2. public / material-tokens / css / theme / light.css

1
.light {
2
  --md-sys-color-primary: rgb(65 95 145);
3
  --md-sys-color-surface-tint: rgb(65 95 145);
4
  --md-sys-color-on-primary: rgb(255 255 255);
5
  --md-sys-color-primary-container: rgb(214 227 255);
6
  --md-sys-color-on-primary-container: rgb(40 71 119);
7
  --md-sys-color-secondary: rgb(86 95 113);
8
  --md-sys-color-on-secondary: rgb(255 255 255);
9
  --md-sys-color-secondary-container: rgb(218 226 249);
10
  --md-sys-color-on-secondary-container: rgb(62 71 89);
11
  --md-sys-color-tertiary: rgb(112 85 117);
12
  --md-sys-color-on-tertiary: rgb(255 255 255);
13
  --md-sys-color-tertiary-container: rgb(250 216 253);
14
  --md-sys-color-on-tertiary-container: rgb(87 62 92);
15
  --md-sys-color-error: rgb(186 26 26);
16
  --md-sys-color-on-error: rgb(255 255 255);
17
  --md-sys-color-error-container: rgb(255 218 214);
18
  --md-sys-color-on-error-container: rgb(147 0 10);
19
  --md-sys-color-background: rgb(249 249 255);
20
  --md-sys-color-on-background: rgb(25 28 32);
21
  --md-sys-color-surface: rgb(249 249 255);
22
  --md-sys-color-on-surface: rgb(25 28 32);
23
  --md-sys-color-surface-variant: rgb(224 226 236);
24
  --md-sys-color-on-surface-variant: rgb(68 71 78);
25
  --md-sys-color-outline: rgb(116 119 127);
26
  --md-sys-color-outline-variant: rgb(196 198 208);
27
  --md-sys-color-shadow: rgb(0 0 0);
28
  --md-sys-color-scrim: rgb(0 0 0);
29
  --md-sys-color-inverse-surface: rgb(46 48 54);
30
  --md-sys-color-inverse-on-surface: rgb(240 240 247);
31
  --md-sys-color-inverse-primary: rgb(170 199 255);
32
  --md-sys-color-primary-fixed: rgb(214 227 255);
33
  --md-sys-color-on-primary-fixed: rgb(0 27 62);
34
  --md-sys-color-primary-fixed-dim: rgb(170 199 255);
35
  --md-sys-color-on-primary-fixed-variant: rgb(40 71 119);
36
  --md-sys-color-secondary-fixed: rgb(218 226 249);
37
  --md-sys-color-on-secondary-fixed: rgb(19 28 43);
38
  --md-sys-color-secondary-fixed-dim: rgb(190 198 220);
39
  --md-sys-color-on-secondary-fixed-variant: rgb(62 71 89);
40
  --md-sys-color-tertiary-fixed: rgb(250 216 253);
41
  --md-sys-color-on-tertiary-fixed: rgb(40 19 46);
42
  --md-sys-color-tertiary-fixed-dim: rgb(221 188 224);
43
  --md-sys-color-on-tertiary-fixed-variant: rgb(87 62 92);
44
  --md-sys-color-surface-dim: rgb(217 217 224);
45
  --md-sys-color-surface-bright: rgb(249 249 255);
46
  --md-sys-color-surface-container-lowest: rgb(255 255 255);
47
  --md-sys-color-surface-container-low: rgb(243 243 250);
48
  --md-sys-color-surface-container: rgb(237 237 244);
49
  --md-sys-color-surface-container-high: rgb(231 232 238);
50
  --md-sys-color-surface-container-highest: rgb(226 226 233);
51
}
52

P. Carpeta « public / ungap »

1. public / ungap / es.js

F. .firebaserc

1
{
2
  "projects": {
3
    "default": "pwamdex"
4
  }
5
}
6

G. firebase.json

1
{
2
  "hosting": {
3
    "public": "public",
4
    "headers": [
5
     {
6
      "source": "**",
7
      "headers": [
8
       {
9
        "key": "Cache-Control",
10
        "value": "no-cache, no-store, must-revalidate"
11
       },
12
       {
13
        "key": "Pragma",
14
        "value": "no-cache"
15
       },
16
       {
17
        "key": "Expires",
18
        "value": "0"
19
       }
20
      ]
21
     }
22
    ],
23
    "ignore": [
24
      "firebase.json",
25
      "**/.*",
26
      "**/node_modules/**"
27
    ]
28
  },
29
  "emulators": {
30
    "hosting": {
31
      "port": 5000
32
    },
33
    "ui": {
34
      "enabled": true
35
    },
36
    "singleProjectMode": true
37
  }
38
}
39

H. LICENSE

1
                    GNU GENERAL PUBLIC LICENSE
2
                       Version 3, 29 June 2007
3
4
 Copyright (C) 2007 Free Software Foundation, Inc. 
5
 Everyone is permitted to copy and distribute verbatim copies
6
 of this license document, but changing it is not allowed.
7
8
                            Preamble
9
10
  The GNU General Public License is a free, copyleft license for
11
software and other kinds of works.
12
13
  The licenses for most software and other practical works are designed
14
to take away your freedom to share and change the works.  By contrast,
15
the GNU General Public License is intended to guarantee your freedom to
16
share and change all versions of a program--to make sure it remains free
17
software for all its users.  We, the Free Software Foundation, use the
18
GNU General Public License for most of our software; it applies also to
19
any other work released this way by its authors.  You can apply it to
20
your programs, too.
21
22
  When we speak of free software, we are referring to freedom, not
23
price.  Our General Public Licenses are designed to make sure that you
24
have the freedom to distribute copies of free software (and charge for
25
them if you wish), that you receive source code or can get it if you
26
want it, that you can change the software or use pieces of it in new
27
free programs, and that you know you can do these things.
28
29
  To protect your rights, we need to prevent others from denying you
30
these rights or asking you to surrender the rights.  Therefore, you have
31
certain responsibilities if you distribute copies of the software, or if
32
you modify it: responsibilities to respect the freedom of others.
33
34
  For example, if you distribute copies of such a program, whether
35
gratis or for a fee, you must pass on to the recipients the same
36
freedoms that you received.  You must make sure that they, too, receive
37
or can get the source code.  And you must show them these terms so they
38
know their rights.
39
40
  Developers that use the GNU GPL protect your rights with two steps:
41
(1) assert copyright on the software, and (2) offer you this License
42
giving you legal permission to copy, distribute and/or modify it.
43
44
  For the developers' and authors' protection, the GPL clearly explains
45
that there is no warranty for this free software.  For both users' and
46
authors' sake, the GPL requires that modified versions be marked as
47
changed, so that their problems will not be attributed erroneously to
48
authors of previous versions.
49
50
  Some devices are designed to deny users access to install or run
51
modified versions of the software inside them, although the manufacturer
52
can do so.  This is fundamentally incompatible with the aim of
53
protecting users' freedom to change the software.  The systematic
54
pattern of such abuse occurs in the area of products for individuals to
55
use, which is precisely where it is most unacceptable.  Therefore, we
56
have designed this version of the GPL to prohibit the practice for those
57
products.  If such problems arise substantially in other domains, we
58
stand ready to extend this provision to those domains in future versions
59
of the GPL, as needed to protect the freedom of users.
60
61
  Finally, every program is threatened constantly by software patents.
62
States should not allow patents to restrict development and use of
63
software on general-purpose computers, but in those that do, we wish to
64
avoid the special danger that patents applied to a free program could
65
make it effectively proprietary.  To prevent this, the GPL assures that
66
patents cannot be used to render the program non-free.
67
68
  The precise terms and conditions for copying, distribution and
69
modification follow.
70
71
                       TERMS AND CONDITIONS
72
73
  0. Definitions.
74
75
  "This License" refers to version 3 of the GNU General Public License.
76
77
  "Copyright" also means copyright-like laws that apply to other kinds of
78
works, such as semiconductor masks.
79
80
  "The Program" refers to any copyrightable work licensed under this
81
License.  Each licensee is addressed as "you".  "Licensees" and
82
"recipients" may be individuals or organizations.
83
84
  To "modify" a work means to copy from or adapt all or part of the work
85
in a fashion requiring copyright permission, other than the making of an
86
exact copy.  The resulting work is called a "modified version" of the
87
earlier work or a work "based on" the earlier work.
88
89
  A "covered work" means either the unmodified Program or a work based
90
on the Program.
91
92
  To "propagate" a work means to do anything with it that, without
93
permission, would make you directly or secondarily liable for
94
infringement under applicable copyright law, except executing it on a
95
computer or modifying a private copy.  Propagation includes copying,
96
distribution (with or without modification), making available to the
97
public, and in some countries other activities as well.
98
99
  To "convey" a work means any kind of propagation that enables other
100
parties to make or receive copies.  Mere interaction with a user through
101
a computer network, with no transfer of a copy, is not conveying.
102
103
  An interactive user interface displays "Appropriate Legal Notices"
104
to the extent that it includes a convenient and prominently visible
105
feature that (1) displays an appropriate copyright notice, and (2)
106
tells the user that there is no warranty for the work (except to the
107
extent that warranties are provided), that licensees may convey the
108
work under this License, and how to view a copy of this License.  If
109
the interface presents a list of user commands or options, such as a
110
menu, a prominent item in the list meets this criterion.
111
112
  1. Source Code.
113
114
  The "source code" for a work means the preferred form of the work
115
for making modifications to it.  "Object code" means any non-source
116
form of a work.
117
118
  A "Standard Interface" means an interface that either is an official
119
standard defined by a recognized standards body, or, in the case of
120
interfaces specified for a particular programming language, one that
121
is widely used among developers working in that language.
122
123
  The "System Libraries" of an executable work include anything, other
124
than the work as a whole, that (a) is included in the normal form of
125
packaging a Major Component, but which is not part of that Major
126
Component, and (b) serves only to enable use of the work with that
127
Major Component, or to implement a Standard Interface for which an
128
implementation is available to the public in source code form.  A
129
"Major Component", in this context, means a major essential component
130
(kernel, window system, and so on) of the specific operating system
131
(if any) on which the executable work runs, or a compiler used to
132
produce the work, or an object code interpreter used to run it.
133
134
  The "Corresponding Source" for a work in object code form means all
135
the source code needed to generate, install, and (for an executable
136
work) run the object code and to modify the work, including scripts to
137
control those activities.  However, it does not include the work's
138
System Libraries, or general-purpose tools or generally available free
139
programs which are used unmodified in performing those activities but
140
which are not part of the work.  For example, Corresponding Source
141
includes interface definition files associated with source files for
142
the work, and the source code for shared libraries and dynamically
143
linked subprograms that the work is specifically designed to require,
144
such as by intimate data communication or control flow between those
145
subprograms and other parts of the work.
146
147
  The Corresponding Source need not include anything that users
148
can regenerate automatically from other parts of the Corresponding
149
Source.
150
151
  The Corresponding Source for a work in source code form is that
152
same work.
153
154
  2. Basic Permissions.
155
156
  All rights granted under this License are granted for the term of
157
copyright on the Program, and are irrevocable provided the stated
158
conditions are met.  This License explicitly affirms your unlimited
159
permission to run the unmodified Program.  The output from running a
160
covered work is covered by this License only if the output, given its
161
content, constitutes a covered work.  This License acknowledges your
162
rights of fair use or other equivalent, as provided by copyright law.
163
164
  You may make, run and propagate covered works that you do not
165
convey, without conditions so long as your license otherwise remains
166
in force.  You may convey covered works to others for the sole purpose
167
of having them make modifications exclusively for you, or provide you
168
with facilities for running those works, provided that you comply with
169
the terms of this License in conveying all material for which you do
170
not control copyright.  Those thus making or running the covered works
171
for you must do so exclusively on your behalf, under your direction
172
and control, on terms that prohibit them from making any copies of
173
your copyrighted material outside their relationship with you.
174
175
  Conveying under any other circumstances is permitted solely under
176
the conditions stated below.  Sublicensing is not allowed; section 10
177
makes it unnecessary.
178
179
  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
181
  No covered work shall be deemed part of an effective technological
182
measure under any applicable law fulfilling obligations under article
183
11 of the WIPO copyright treaty adopted on 20 December 1996, or
184
similar laws prohibiting or restricting circumvention of such
185
measures.
186
187
  When you convey a covered work, you waive any legal power to forbid
188
circumvention of technological measures to the extent such circumvention
189
is effected by exercising rights under this License with respect to
190
the covered work, and you disclaim any intention to limit operation or
191
modification of the work as a means of enforcing, against the work's
192
users, your or third parties' legal rights to forbid circumvention of
193
technological measures.
194
195
  4. Conveying Verbatim Copies.
196
197
  You may convey verbatim copies of the Program's source code as you
198
receive it, in any medium, provided that you conspicuously and
199
appropriately publish on each copy an appropriate copyright notice;
200
keep intact all notices stating that this License and any
201
non-permissive terms added in accord with section 7 apply to the code;
202
keep intact all notices of the absence of any warranty; and give all
203
recipients a copy of this License along with the Program.
204
205
  You may charge any price or no price for each copy that you convey,
206
and you may offer support or warranty protection for a fee.
207
208
  5. Conveying Modified Source Versions.
209
210
  You may convey a work based on the Program, or the modifications to
211
produce it from the Program, in the form of source code under the
212
terms of section 4, provided that you also meet all of these conditions:
213
214
    a) The work must carry prominent notices stating that you modified
215
    it, and giving a relevant date.
216
217
    b) The work must carry prominent notices stating that it is
218
    released under this License and any conditions added under section
219
    7.  This requirement modifies the requirement in section 4 to
220
    "keep intact all notices".
221
222
    c) You must license the entire work, as a whole, under this
223
    License to anyone who comes into possession of a copy.  This
224
    License will therefore apply, along with any applicable section 7
225
    additional terms, to the whole of the work, and all its parts,
226
    regardless of how they are packaged.  This License gives no
227
    permission to license the work in any other way, but it does not
228
    invalidate such permission if you have separately received it.
229
230
    d) If the work has interactive user interfaces, each must display
231
    Appropriate Legal Notices; however, if the Program has interactive
232
    interfaces that do not display Appropriate Legal Notices, your
233
    work need not make them do so.
234
235
  A compilation of a covered work with other separate and independent
236
works, which are not by their nature extensions of the covered work,
237
and which are not combined with it such as to form a larger program,
238
in or on a volume of a storage or distribution medium, is called an
239
"aggregate" if the compilation and its resulting copyright are not
240
used to limit the access or legal rights of the compilation's users
241
beyond what the individual works permit.  Inclusion of a covered work
242
in an aggregate does not cause this License to apply to the other
243
parts of the aggregate.
244
245
  6. Conveying Non-Source Forms.
246
247
  You may convey a covered work in object code form under the terms
248
of sections 4 and 5, provided that you also convey the
249
machine-readable Corresponding Source under the terms of this License,
250
in one of these ways:
251
252
    a) Convey the object code in, or embodied in, a physical product
253
    (including a physical distribution medium), accompanied by the
254
    Corresponding Source fixed on a durable physical medium
255
    customarily used for software interchange.
256
257
    b) Convey the object code in, or embodied in, a physical product
258
    (including a physical distribution medium), accompanied by a
259
    written offer, valid for at least three years and valid for as
260
    long as you offer spare parts or customer support for that product
261
    model, to give anyone who possesses the object code either (1) a
262
    copy of the Corresponding Source for all the software in the
263
    product that is covered by this License, on a durable physical
264
    medium customarily used for software interchange, for a price no
265
    more than your reasonable cost of physically performing this
266
    conveying of source, or (2) access to copy the
267
    Corresponding Source from a network server at no charge.
268
269
    c) Convey individual copies of the object code with a copy of the
270
    written offer to provide the Corresponding Source.  This
271
    alternative is allowed only occasionally and noncommercially, and
272
    only if you received the object code with such an offer, in accord
273
    with subsection 6b.
274
275
    d) Convey the object code by offering access from a designated
276
    place (gratis or for a charge), and offer equivalent access to the
277
    Corresponding Source in the same way through the same place at no
278
    further charge.  You need not require recipients to copy the
279
    Corresponding Source along with the object code.  If the place to
280
    copy the object code is a network server, the Corresponding Source
281
    may be on a different server (operated by you or a third party)
282
    that supports equivalent copying facilities, provided you maintain
283
    clear directions next to the object code saying where to find the
284
    Corresponding Source.  Regardless of what server hosts the
285
    Corresponding Source, you remain obligated to ensure that it is
286
    available for as long as needed to satisfy these requirements.
287
288
    e) Convey the object code using peer-to-peer transmission, provided
289
    you inform other peers where the object code and Corresponding
290
    Source of the work are being offered to the general public at no
291
    charge under subsection 6d.
292
293
  A separable portion of the object code, whose source code is excluded
294
from the Corresponding Source as a System Library, need not be
295
included in conveying the object code work.
296
297
  A "User Product" is either (1) a "consumer product", which means any
298
tangible personal property which is normally used for personal, family,
299
or household purposes, or (2) anything designed or sold for incorporation
300
into a dwelling.  In determining whether a product is a consumer product,
301
doubtful cases shall be resolved in favor of coverage.  For a particular
302
product received by a particular user, "normally used" refers to a
303
typical or common use of that class of product, regardless of the status
304
of the particular user or of the way in which the particular user
305
actually uses, or expects or is expected to use, the product.  A product
306
is a consumer product regardless of whether the product has substantial
307
commercial, industrial or non-consumer uses, unless such uses represent
308
the only significant mode of use of the product.
309
310
  "Installation Information" for a User Product means any methods,
311
procedures, authorization keys, or other information required to install
312
and execute modified versions of a covered work in that User Product from
313
a modified version of its Corresponding Source.  The information must
314
suffice to ensure that the continued functioning of the modified object
315
code is in no case prevented or interfered with solely because
316
modification has been made.
317
318
  If you convey an object code work under this section in, or with, or
319
specifically for use in, a User Product, and the conveying occurs as
320
part of a transaction in which the right of possession and use of the
321
User Product is transferred to the recipient in perpetuity or for a
322
fixed term (regardless of how the transaction is characterized), the
323
Corresponding Source conveyed under this section must be accompanied
324
by the Installation Information.  But this requirement does not apply
325
if neither you nor any third party retains the ability to install
326
modified object code on the User Product (for example, the work has
327
been installed in ROM).
328
329
  The requirement to provide Installation Information does not include a
330
requirement to continue to provide support service, warranty, or updates
331
for a work that has been modified or installed by the recipient, or for
332
the User Product in which it has been modified or installed.  Access to a
333
network may be denied when the modification itself materially and
334
adversely affects the operation of the network or violates the rules and
335
protocols for communication across the network.
336
337
  Corresponding Source conveyed, and Installation Information provided,
338
in accord with this section must be in a format that is publicly
339
documented (and with an implementation available to the public in
340
source code form), and must require no special password or key for
341
unpacking, reading or copying.
342
343
  7. Additional Terms.
344
345
  "Additional permissions" are terms that supplement the terms of this
346
License by making exceptions from one or more of its conditions.
347
Additional permissions that are applicable to the entire Program shall
348
be treated as though they were included in this License, to the extent
349
that they are valid under applicable law.  If additional permissions
350
apply only to part of the Program, that part may be used separately
351
under those permissions, but the entire Program remains governed by
352
this License without regard to the additional permissions.
353
354
  When you convey a copy of a covered work, you may at your option
355
remove any additional permissions from that copy, or from any part of
356
it.  (Additional permissions may be written to require their own
357
removal in certain cases when you modify the work.)  You may place
358
additional permissions on material, added by you to a covered work,
359
for which you have or can give appropriate copyright permission.
360
361
  Notwithstanding any other provision of this License, for material you
362
add to a covered work, you may (if authorized by the copyright holders of
363
that material) supplement the terms of this License with terms:
364
365
    a) Disclaiming warranty or limiting liability differently from the
366
    terms of sections 15 and 16 of this License; or
367
368
    b) Requiring preservation of specified reasonable legal notices or
369
    author attributions in that material or in the Appropriate Legal
370
    Notices displayed by works containing it; or
371
372
    c) Prohibiting misrepresentation of the origin of that material, or
373
    requiring that modified versions of such material be marked in
374
    reasonable ways as different from the original version; or
375
376
    d) Limiting the use for publicity purposes of names of licensors or
377
    authors of the material; or
378
379
    e) Declining to grant rights under trademark law for use of some
380
    trade names, trademarks, or service marks; or
381
382
    f) Requiring indemnification of licensors and authors of that
383
    material by anyone who conveys the material (or modified versions of
384
    it) with contractual assumptions of liability to the recipient, for
385
    any liability that these contractual assumptions directly impose on
386
    those licensors and authors.
387
388
  All other non-permissive additional terms are considered "further
389
restrictions" within the meaning of section 10.  If the Program as you
390
received it, or any part of it, contains a notice stating that it is
391
governed by this License along with a term that is a further
392
restriction, you may remove that term.  If a license document contains
393
a further restriction but permits relicensing or conveying under this
394
License, you may add to a covered work material governed by the terms
395
of that license document, provided that the further restriction does
396
not survive such relicensing or conveying.
397
398
  If you add terms to a covered work in accord with this section, you
399
must place, in the relevant source files, a statement of the
400
additional terms that apply to those files, or a notice indicating
401
where to find the applicable terms.
402
403
  Additional terms, permissive or non-permissive, may be stated in the
404
form of a separately written license, or stated as exceptions;
405
the above requirements apply either way.
406
407
  8. Termination.
408
409
  You may not propagate or modify a covered work except as expressly
410
provided under this License.  Any attempt otherwise to propagate or
411
modify it is void, and will automatically terminate your rights under
412
this License (including any patent licenses granted under the third
413
paragraph of section 11).
414
415
  However, if you cease all violation of this License, then your
416
license from a particular copyright holder is reinstated (a)
417
provisionally, unless and until the copyright holder explicitly and
418
finally terminates your license, and (b) permanently, if the copyright
419
holder fails to notify you of the violation by some reasonable means
420
prior to 60 days after the cessation.
421
422
  Moreover, your license from a particular copyright holder is
423
reinstated permanently if the copyright holder notifies you of the
424
violation by some reasonable means, this is the first time you have
425
received notice of violation of this License (for any work) from that
426
copyright holder, and you cure the violation prior to 30 days after
427
your receipt of the notice.
428
429
  Termination of your rights under this section does not terminate the
430
licenses of parties who have received copies or rights from you under
431
this License.  If your rights have been terminated and not permanently
432
reinstated, you do not qualify to receive new licenses for the same
433
material under section 10.
434
435
  9. Acceptance Not Required for Having Copies.
436
437
  You are not required to accept this License in order to receive or
438
run a copy of the Program.  Ancillary propagation of a covered work
439
occurring solely as a consequence of using peer-to-peer transmission
440
to receive a copy likewise does not require acceptance.  However,
441
nothing other than this License grants you permission to propagate or
442
modify any covered work.  These actions infringe copyright if you do
443
not accept this License.  Therefore, by modifying or propagating a
444
covered work, you indicate your acceptance of this License to do so.
445
446
  10. Automatic Licensing of Downstream Recipients.
447
448
  Each time you convey a covered work, the recipient automatically
449
receives a license from the original licensors, to run, modify and
450
propagate that work, subject to this License.  You are not responsible
451
for enforcing compliance by third parties with this License.
452
453
  An "entity transaction" is a transaction transferring control of an
454
organization, or substantially all assets of one, or subdividing an
455
organization, or merging organizations.  If propagation of a covered
456
work results from an entity transaction, each party to that
457
transaction who receives a copy of the work also receives whatever
458
licenses to the work the party's predecessor in interest had or could
459
give under the previous paragraph, plus a right to possession of the
460
Corresponding Source of the work from the predecessor in interest, if
461
the predecessor has it or can get it with reasonable efforts.
462
463
  You may not impose any further restrictions on the exercise of the
464
rights granted or affirmed under this License.  For example, you may
465
not impose a license fee, royalty, or other charge for exercise of
466
rights granted under this License, and you may not initiate litigation
467
(including a cross-claim or counterclaim in a lawsuit) alleging that
468
any patent claim is infringed by making, using, selling, offering for
469
sale, or importing the Program or any portion of it.
470
471
  11. Patents.
472
473
  A "contributor" is a copyright holder who authorizes use under this
474
License of the Program or a work on which the Program is based.  The
475
work thus licensed is called the contributor's "contributor version".
476
477
  A contributor's "essential patent claims" are all patent claims
478
owned or controlled by the contributor, whether already acquired or
479
hereafter acquired, that would be infringed by some manner, permitted
480
by this License, of making, using, or selling its contributor version,
481
but do not include claims that would be infringed only as a
482
consequence of further modification of the contributor version.  For
483
purposes of this definition, "control" includes the right to grant
484
patent sublicenses in a manner consistent with the requirements of
485
this License.
486
487
  Each contributor grants you a non-exclusive, worldwide, royalty-free
488
patent license under the contributor's essential patent claims, to
489
make, use, sell, offer for sale, import and otherwise run, modify and
490
propagate the contents of its contributor version.
491
492
  In the following three paragraphs, a "patent license" is any express
493
agreement or commitment, however denominated, not to enforce a patent
494
(such as an express permission to practice a patent or covenant not to
495
sue for patent infringement).  To "grant" such a patent license to a
496
party means to make such an agreement or commitment not to enforce a
497
patent against the party.
498
499
  If you convey a covered work, knowingly relying on a patent license,
500
and the Corresponding Source of the work is not available for anyone
501
to copy, free of charge and under the terms of this License, through a
502
publicly available network server or other readily accessible means,
503
then you must either (1) cause the Corresponding Source to be so
504
available, or (2) arrange to deprive yourself of the benefit of the
505
patent license for this particular work, or (3) arrange, in a manner
506
consistent with the requirements of this License, to extend the patent
507
license to downstream recipients.  "Knowingly relying" means you have
508
actual knowledge that, but for the patent license, your conveying the
509
covered work in a country, or your recipient's use of the covered work
510
in a country, would infringe one or more identifiable patents in that
511
country that you have reason to believe are valid.
512
513
  If, pursuant to or in connection with a single transaction or
514
arrangement, you convey, or propagate by procuring conveyance of, a
515
covered work, and grant a patent license to some of the parties
516
receiving the covered work authorizing them to use, propagate, modify
517
or convey a specific copy of the covered work, then the patent license
518
you grant is automatically extended to all recipients of the covered
519
work and works based on it.
520
521
  A patent license is "discriminatory" if it does not include within
522
the scope of its coverage, prohibits the exercise of, or is
523
conditioned on the non-exercise of one or more of the rights that are
524
specifically granted under this License.  You may not convey a covered
525
work if you are a party to an arrangement with a third party that is
526
in the business of distributing software, under which you make payment
527
to the third party based on the extent of your activity of conveying
528
the work, and under which the third party grants, to any of the
529
parties who would receive the covered work from you, a discriminatory
530
patent license (a) in connection with copies of the covered work
531
conveyed by you (or copies made from those copies), or (b) primarily
532
for and in connection with specific products or compilations that
533
contain the covered work, unless you entered into that arrangement,
534
or that patent license was granted, prior to 28 March 2007.
535
536
  Nothing in this License shall be construed as excluding or limiting
537
any implied license or other defenses to infringement that may
538
otherwise be available to you under applicable patent law.
539
540
  12. No Surrender of Others' Freedom.
541
542
  If conditions are imposed on you (whether by court order, agreement or
543
otherwise) that contradict the conditions of this License, they do not
544
excuse you from the conditions of this License.  If you cannot convey a
545
covered work so as to satisfy simultaneously your obligations under this
546
License and any other pertinent obligations, then as a consequence you may
547
not convey it at all.  For example, if you agree to terms that obligate you
548
to collect a royalty for further conveying from those to whom you convey
549
the Program, the only way you could satisfy both those terms and this
550
License would be to refrain entirely from conveying the Program.
551
552
  13. Use with the GNU Affero General Public License.
553
554
  Notwithstanding any other provision of this License, you have
555
permission to link or combine any covered work with a work licensed
556
under version 3 of the GNU Affero General Public License into a single
557
combined work, and to convey the resulting work.  The terms of this
558
License will continue to apply to the part which is the covered work,
559
but the special requirements of the GNU Affero General Public License,
560
section 13, concerning interaction through a network will apply to the
561
combination as such.
562
563
  14. Revised Versions of this License.
564
565
  The Free Software Foundation may publish revised and/or new versions of
566
the GNU General Public License from time to time.  Such new versions will
567
be similar in spirit to the present version, but may differ in detail to
568
address new problems or concerns.
569
570
  Each version is given a distinguishing version number.  If the
571
Program specifies that a certain numbered version of the GNU General
572
Public License "or any later version" applies to it, you have the
573
option of following the terms and conditions either of that numbered
574
version or of any later version published by the Free Software
575
Foundation.  If the Program does not specify a version number of the
576
GNU General Public License, you may choose any version ever published
577
by the Free Software Foundation.
578
579
  If the Program specifies that a proxy can decide which future
580
versions of the GNU General Public License can be used, that proxy's
581
public statement of acceptance of a version permanently authorizes you
582
to choose that version for the Program.
583
584
  Later license versions may give you additional or different
585
permissions.  However, no additional obligations are imposed on any
586
author or copyright holder as a result of your choosing to follow a
587
later version.
588
589
  15. Disclaimer of Warranty.
590
591
  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
600
  16. Limitation of Liability.
601
602
  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610
SUCH DAMAGES.
611
612
  17. Interpretation of Sections 15 and 16.
613
614
  If the disclaimer of warranty and limitation of liability provided
615
above cannot be given local legal effect according to their terms,
616
reviewing courts shall apply local law that most closely approximates
617
an absolute waiver of all civil liability in connection with the
618
Program, unless a warranty or assumption of liability accompanies a
619
copy of the Program in return for a fee.
620
621
                     END OF TERMS AND CONDITIONS
622
623
            How to Apply These Terms to Your New Programs
624
625
  If you develop a new program, and you want it to be of the greatest
626
possible use to the public, the best way to achieve this is to make it
627
free software which everyone can redistribute and change under these terms.
628
629
  To do so, attach the following notices to the program.  It is safest
630
to attach them to the start of each source file to most effectively
631
state the exclusion of warranty; and each file should have at least
632
the "copyright" line and a pointer to where the full notice is found.
633
634
    
635
    Copyright (C)   
636
637
    This program is free software: you can redistribute it and/or modify
638
    it under the terms of the GNU General Public License as published by
639
    the Free Software Foundation, either version 3 of the License, or
640
    (at your option) any later version.
641
642
    This program is distributed in the hope that it will be useful,
643
    but WITHOUT ANY WARRANTY; without even the implied warranty of
644
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
645
    GNU General Public License for more details.
646
647
    You should have received a copy of the GNU General Public License
648
    along with this program.  If not, see .
649
650
Also add information on how to contact you by electronic and paper mail.
651
652
  If the program does terminal interaction, make it output a short
653
notice like this when it starts in an interactive mode:
654
655
      Copyright (C)   
656
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657
    This is free software, and you are welcome to redistribute it
658
    under certain conditions; type `show c' for details.
659
660
The hypothetical commands `show w' and `show c' should show the appropriate
661
parts of the General Public License.  Of course, your program's commands
662
might be different; for a GUI interface, you would use an "about box".
663
664
  You should also get your employer (if you work as a programmer) or school,
665
if any, to sign a "copyright disclaimer" for the program, if necessary.
666
For more information on this, and how to apply and follow the GNU GPL, see
667
.
668
669
  The GNU General Public License does not permit incorporating your program
670
into proprietary programs.  If your program is a subroutine library, you
671
may consider it more useful to permit linking proprietary applications with
672
the library.  If this is what you want to do, use the GNU Lesser General
673
Public License instead of this License.  But first, please read
674
.
675

I. README.md

1
# pwamdex
2
PWA con M3 Expressive
3

J. jsconfig.json

1
{
2
 "compilerOptions": {
3
  "checkJs": true,
4
  "strictNullChecks": true,
5
  "target": "ES6",
6
  "module": "Node16",
7
  "moduleResolution": "Node16",
8
  "lib": [
9
   "ES2017",
10
   "WebWorker",
11
   "DOM"
12
  ]
18
}

K. .gitignore

1
# Logs
2
logs
3
*.log
4
npm-debug.log*
5
yarn-debug.log*
6
yarn-error.log*
7
firebase-debug.log*
8
firebase-debug.*.log*
9
10
# Firebase cache
11
.firebase/
12
13
# Firebase config
14
15
# Uncomment this if you'd like others to create their own Firebase project.
16
# For a team working on the same Firebase project(s), it is recommended to leave
17
# it commented so all members can deploy to the same project(s) in .firebaserc.
18
# .firebaserc
19
20
# Runtime data
21
pids
22
*.pid
23
*.seed
24
*.pid.lock
25
26
# Directory for instrumented libs generated by jscoverage/JSCover
27
lib-cov
28
29
# Coverage directory used by tools like istanbul
30
coverage
31
32
# nyc test coverage
33
.nyc_output
34
35
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
36
.grunt
37
38
# Bower dependency directory (https://bower.io/)
39
bower_components
40
41
# node-waf configuration
42
.lock-wscript
43
44
# Compiled binary addons (http://nodejs.org/api/addons.html)
45
build/Release
46
47
# Dependency directories
48
node_modules/
49
50
# Optional npm cache directory
51
.npm
52
53
# Optional eslint cache
54
.eslintcache
55
56
# Optional REPL history
57
.node_repl_history
58
59
# Output of 'npm pack'
60
*.tgz
61
62
# Yarn Integrity file
63
.yarn-integrity
64
65
# dotenv environment variables file
66
.env
67
68
# dataconnect generated files
69
.dataconnect
70

L. generar-listado-sw.js

1
const fs = require('fs/promises');
2
const path = require('path');
3
4
// Configuración de rutas
5
const PUBLIC_DIR = 'public';
6
const OUTPUT_FILE = 'lista_archivos_sw.txt';
7
8
// Restricciones basadas en tus instrucciones (Paso 4)
9
const EXCLUDED_DIRS = ['.vscode'];
10
const EXCLUDED_FILES = [
11
 '.firebaserc',
12
 '.gitignore',
13
 '.htaccess',
14
 '404.html',
15
 'sw.js',
16
 'lista_archivos_sw.txt',
17
 'generar-listado-sw.js',
18
 'firebase.json',
19
 'jsconfig.json',
20
 'LICENSE',
21
 'README.md',
22
];
23
const EXCLUDED_EXTENSIONS = ['.php', '.db'];
24
25
/**
26
 * Función recursiva para obtener todos los archivos de un directorio
27
 * @param {import("node:fs").PathLike} dir
28
 */
29
async function getFiles(dir) {
30
 /**
31
  * @type {any[]}
32
  */
33
 let results = [];
34
 const list = await fs.readdir(dir, { withFileTypes: true });
35
36
 for (const dirent of list) {
37
  const fullPath = path.join(dir, dirent.name);
38
39
  if (dirent.isDirectory()) {
40
   // Ignorar carpetas excluidas como .vscode
41
   if (!EXCLUDED_DIRS.includes(dirent.name)) {
42
    results = results.concat(await getFiles(fullPath));
43
   }
44
  } else {
45
   const fileName = dirent.name;
46
   const ext = path.extname(fileName).toLowerCase();
47
48
   // Filtrar archivos específicos y extensiones (.php, .db)
49
   if (!EXCLUDED_FILES.includes(fileName) && !EXCLUDED_EXTENSIONS.includes(ext)) {
50
51
    // Obtener ruta relativa respecto a la carpeta 'public'
52
    let relativePath = path.relative(PUBLIC_DIR, fullPath);
53
54
    // Convertir barras invertidas de Windows (\) a barras normales (/) (Paso 5 y 8)
55
    relativePath = relativePath.split(path.sep).join('/');
56
57
    // Guardar la ruta con el formato adecuado
58
    results.push(`"${ relativePath }"`);
59
   }
60
  }
61
 }
62
 return results;
63
}
64
65
/**
66
 * Función principal para generar el archivo
67
 */
68
async function generateSWList() {
69
 try {
70
  console.log(`Explorando la carpeta "${ PUBLIC_DIR }"...`);
71
  const files = await getFiles(PUBLIC_DIR);
72
73
  // Mantener el último elemento requerido por el Service Worker (Paso 10)
74
  files.push('"/"');
75
76
  // Dar formato de arreglo de JavaScript con saltos de línea y sangría (Paso 6 y 9)
77
  const arrayContent = `const ARCHIVOS = [\n  ${ files.join(',\n  ') }\n]`;
78
79
  // Escribir el resultado en un archivo de texto
80
  await fs.writeFile(OUTPUT_FILE, arrayContent, 'utf-8');
81
82
  console.log(`¡Éxito! El listado se ha generado correctamente.`);
83
  console.log(`Revisa el archivo "${ OUTPUT_FILE }" y copia el contenido a tu public/sw.js.`);
84
85
 } catch (error) {
86
  console.error('Error al generar el listado:', error.message);
87
 }
88
}
89
90
generateSWList();

M. lista_archivos_sw.txt

1
const ARCHIVOS = [
2
  "ayuda.html",
3
  "css/estilos.css",
4
  "css/material-symbols-outlined.css",
5
  "css/transicion_pestanas.css",
6
  "favicon.ico",
7
  "fonts/MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].codepoints",
8
  "fonts/MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].ttf",
9
  "fonts/MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].woff2",
10
  "fonts/Roboto-Italic-VariableFont_wdth,wght.ttf",
11
  "fonts/Roboto-VariableFont_wdth,wght.ttf",
12
  "img/icono2048.png",
13
  "img/maskable_icon.png",
14
  "img/maskable_icon_x128.png",
15
  "img/maskable_icon_x192.png",
16
  "img/maskable_icon_x384.png",
17
  "img/maskable_icon_x48.png",
18
  "img/maskable_icon_x512.png",
19
  "img/maskable_icon_x72.png",
20
  "img/maskable_icon_x96.png",
21
  "img/screenshot_horizontal.png",
22
  "img/screenshot_vertical.png",
23
  "index.html",
24
  "js/nav-tab-fixed.js",
25
  "js/registraServiceWorker.js",
26
  "libclienteweb/abreElementoHtml.js",
27
  "libclienteweb/cierraElementoHtmo.js",
28
  "libclienteweb/ES_APPLE.js",
29
  "libclienteweb/getAttribute.js",
30
  "libclienteweb/manejaErrores.js",
31
  "libclienteweb/muestraError.js",
32
  "libclienteweb/muestraTextoDeAyuda.js",
33
  "libclienteweb/ProblemDetailsError.js",
34
  "libclienteweb/querySelector.js",
35
  "libclienteweb/resaltaSiEstasEn.js",
36
  "libmde/md-app-bar.js",
37
  "libmde/md-filled-button.css",
38
  "libmde/md-filled-text-field.css",
39
  "libmde/md-list.css",
40
  "libmde/md-menu.css",
41
  "libmde/md-options-menu.js",
42
  "libmde/md-outline-button.css",
43
  "libmde/md-select-menu.js",
44
  "libmde/md-tab.css",
45
  "material-tokens/css/baseline.css",
46
  "material-tokens/css/colors.css",
47
  "material-tokens/css/elevation.css",
48
  "material-tokens/css/motion.css",
49
  "material-tokens/css/palette.css",
50
  "material-tokens/css/shape.css",
51
  "material-tokens/css/state.css",
52
  "material-tokens/css/theme/dark.css",
53
  "material-tokens/css/theme/light.css",
54
  "material-tokens/css/typography.css",
55
  "select.html",
56
  "site.webmanifest",
57
  "ungap/es.js",
58
  "/"
59
]

N. Resumen