32. Carpeta « lib »

Versión para imprimir.

A. Carpeta « lib / css »

Versión para imprimir.

1. lib / css / material-symbols-outlined.css

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

2. lib / css / md-cards.css

1.md-cards {
2 margin: 0.5rem;
3 gap: 0.5rem;
4 display: grid;
5 grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
6}
7
8/* container */
9.md-cards>*::before {
10 content: "";
11 position: absolute;
12 z-index: -2;
13 top: 0;
14 right: 0;
15 left: 0;
16 bottom: 0;
17 background-color: var(--md-sys-color-surface-variant);
18}
19
20/* state layer */
21.md-cards>*::after {
22 content: "";
23 position: absolute;
24 z-index: -1;
25 top: 0;
26 right: 0;
27 left: 0;
28 bottom: 0;
29 background-color: transparent;
30}
31
32.md-cards>* {
33 position: relative;
34 display: block;
35 text-decoration: none;
36 color: var(--md-sys-color-on-surface-variant);
37 border-radius: 0.75rem;
38 overflow: hidden;
39 box-shadow: var(--md-box_shadow_level0);
40}
41
42/* state layer */
43.md-cards>:hover::after {
44 background-color: var(--md-sys-color-on-surface-variant);
45 opacity: var(--md-sys-state-hover-state-layer-opacity);
46}
47
48.md-cards>a:hover {
49 box-shadow: var(--md-box_shadow_level1);
50}
51
52/* state layer */
53.md-cards>:focus::after {
54 background-color: var(--md-sys-color-on-surface-variant);
55 opacity: var(--md-sys-state-focus-state-layer-opacity);
56}
57
58.md-cards>a:focus {
59 outline: none;
60}
61
62/* state layer */
63.md-cards>:active::after {
64 background-color: var(--md-sys-color-on-surface-variant);
65 opacity: var(--md-sys-state-pressed-state-layer-opacity);
66}
67
68.md-cards a:active {
69 background-position: center;
70 background-image:
71 radial-gradient(circle, var(--md-riple-color) 1%, transparent 1%);
72 background-size: 100%;
73 animation-name: md-ripple;
74 animation-duration: var(--md-sys-motion-duration-500);
75 box-shadow: var(--md-box_shadow_level0) !important;
76}
77
78.md-cards>*>* {
79 display: block;
80}
81
82.md-cards figure {
83 border-radius: 0.75rem;
84 padding: 0;
85 margin: 0;
86 width: 100%;
87}
88
89.md-cards figure * {
90 border-radius: 0.75rem;
91 width: 100%;
92}
93
94.md-cards .headline {
95 margin: 1rem;
96 font-family: var(--md-sys-typescale-headline-small-font);
97 font-weight: var(--md-sys-typescale-headline-small-weight);
98 font-size: var(--md-sys-typescale-headline-small-size);
99 font-style: var(--md-sys-typescale-headline-small-font-style);
100 letter-spacing: var(--md-sys-typescale-headline-small-tracking);
101 line-height: var(--md-sys-typescale-headline-small-line-height);
102 text-transform: var(--md-sys-typescale-headline-small-text-transform);
103 text-decoration: var(--md-sys-typescale-headline-small-text-decoration);
104}
105
106.md-cards a .headline {
107 text-decoration: underline;
108}
109
110.md-cards .supporting {
111 margin: 1rem;
112 font-family: var(--md-sys-typescale-body-large-font);
113 font-weight: var(--md-sys-typescale-body-large-weight);
114 font-size: var(--md-sys-typescale-body-large-size);
115 font-style: var(--md-sys-typescale-body-large-font-style);
116 letter-spacing: var(--md-sys-typescale-body-large-tracking);
117 line-height: var(--md-sys-typescale-body-large-line-height);
118 text-transform: var(--md-sys-typescale-body-large-text-transform);
119 text-decoration: var(--md-sys-typescale-body-large-text-decoration);
120}

3. lib / css / md-fab-primary.css

1.md-fab-primary {
2 position: relative;
3 display: inline-block;
4 border: none;
5 width: 3.5rem;
6 height: 3.5rem;
7 border-radius: var(--md-sys-shape-corner-large-default-size);
8 overflow: hidden;
9 padding: 0;
10 padding-block: 0;
11 padding-inline: 0;
12 text-decoration: none;
13 background-color: var(--md-sys-color-primary-container);
14 box-shadow: var(--md-box_shadow_level3);
15}
16
17.md-fab-primary[hidden] {
18 display: none;
19}
20
21/* state layer */
22.md-fab-primary::after {
23 content: "";
24 position: absolute;
25 top: 0;
26 right: 0;
27 left: 0;
28 bottom: 0;
29}
30
31.md-fab-primary span {
32 position: relative;
33 color: var(--md-sys-color-on-primary-container);
34}
35
36.md-fab-primary:hover {
37 box-shadow: var(--md-box_shadow_level4);
38}
39
40.md-fab-primary:hover::after {
41 background-color: var(--md-sys-color-on-primary-container);
42 opacity: var(--md-sys-state-hover-state-layer-opacity);
43}
44
45.md-fab-primary:hover span {
46 color: var(--md-sys-color-on-primary-container);
47}
48
49
50.md-fab-primary:focus {
51 box-shadow: var(--md-box_shadow_level3);
52 outline:none;
53 /* outline:
54 var(--md-sys-color-secondary) var(--md-sys-state-focus-indicator-thickness);
55 outline-offset: var(--md-sys-state-focus-indicator-outer-offset); */
56}
57
58.md-fab-primary:focus::after {
59 background-color: var(--md-sys-color-on-primary-container);
60 opacity: var(--md-sys-state-focus-state-layer-opacity);
61}
62
63.md-fab-primary:focus span {
64 color: var(--md-sys-color-on-primary-container);
65}
66
67.md-fab-primary:active {
68 box-shadow: var(--md-box_shadow_level3);
69 background-position: center;
70 background-image:
71 radial-gradient(circle, var(--md-riple-color) 1%, transparent 1%);
72 background-size: 100%;
73 animation-name: md-ripple;
74 animation-duration: var(--md-sys-motion-duration-500);
75}
76
77.md-fab-primary:active::after {
78 background-color: var(--md-sys-color-on-primary-container);
79 opacity: var(--md-sys-state-pressed-state-layer-opacity);
80}
81
82.md-fab-primary:active span {
83 color: var(--md-sys-color-on-primary-container);
84}

4. lib / css / 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}

5. lib / css / 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 :not(:placeholder-shown)+span,
62.md-filled-text-field :not(:placeholder-shown)+label,
63.md-filled-text-field .populated+span,
64.md-filled-text-field .populated+label,
65.md-filled-text-field:focus-within span,
66.md-filled-text-field:focus-within label,
67.md-filled-text-field.float span,
68.md-filled-text-field.float label {
69 transform: translateY(0);
70 font-family: var(--md-sys-typescale-body-small-font);
71 font-weight: var(--md-sys-typescale-body-small-weight);
72 font-size: var(--md-sys-typescale-body-small-size);
73 font-style: var(--md-sys-typescale-body-small-font-style);
74 letter-spacing: var(--md-sys-typescale-body-small-tracking);
75 line-height: var(--md-sys-typescale-body-small-line-height);
76 text-transform: var(--md-sys-typescale-body-small-text-transform);
77 text-decoration: var(--md-sys-typescale-body-small-text-decoration);
78}
79
80.md-filled-text-field :not(label, span, small) {
81 position: relative;
82 caret-color: var(--md-sys-color-primary);
83 min-height: 2rem;
84 box-sizing: border-box;
85 padding-left: 1rem;
86 padding-bottom: 0.5rem;
87 padding-right: 1rem;
88 border: none;
89 resize: none;
90 color: var(--md-sys-color-on-surface);
91 font-family: var(--md-sys-typescale-body-large-font);
92 font-weight: var(--md-sys-typescale-body-large-weight);
93 font-size: var(--md-sys-typescale-body-large-size);
94 font-style: var(--md-sys-typescale-body-large-font-style);
95 letter-spacing: var(--md-sys-typescale-body-large-tracking);
96 line-height: var(--md-sys-typescale-body-large-line-height);
97 text-transform: var(--md-sys-typescale-body-large-text-transform);
98 text-decoration: var(--md-sys-typescale-body-large-text-decoration);
99 background-color: transparent;
100 outline: none;
101 border-bottom-width: 0.0625rem;
102 border-bottom-style: solid;
103 border-bottom-color: var(--md-sys-color-on-surface-variant);
104}
105
106.md-filled-text-field ::placeholder {
107 color: transparent;
108}
109
110.md-filled-text-field small {
111 display: block;
112 color: var(--md-sys-color-on-surface-variant);
113 background-color: var(--md-sys-color-background);
114 font-family: var(--md-sys-typescale-body-small-font);
115 font-weight: var(--md-sys-typescale-body-small-weight);
116 font-size: var(--md-sys-typescale-body-small-size);
117 font-style: var(--md-sys-typescale-body-small-font-style);
118 letter-spacing: var(--md-sys-typescale-body-small-tracking);
119 line-height: var(--md-sys-typescale-body-small-line-height);
120 text-transform: var(--md-sys-typescale-body-small-text-transform);
121 text-decoration: var(--md-sys-typescale-body-small-text-decoration);
122 padding: 0.25rem 1rem 0 1rem;
123 white-space: nowrap;
124 text-overflow: ellipsis;
125 overflow: hidden;
126}
127
128.md-filled-text-field:hover span,
129.md-filled-text-field:hover label {
130 color: var(--md-sys-color-on-surface-variant);
131}
132
133.md-filled-text-field:hover :not(label, span, small) {
134 padding-bottom: 0.5rem;
135 border-bottom-width: 0.0625rem;
136 border-bottom-color: var(--md-sys-color-on-surface);
137}
138
139.md-filled-text-field:hover::after {
140 background-color: var(--md-sys-color-on-surface);
141 opacity: var(--md-sys-state-hover-state-layer-opacity);
142}
143
144.md-filled-text-field:hover small {
145 color: var(--md-sys-color-on-surface-variant);
146}
147
148.md-filled-text-field:focus-within span,
149.md-filled-text-field:focus-within label {
150 color: var(--md-sys-color-primary);
151}
152
153.md-filled-text-field :focus {
154 color: var(--md-sys-color-on-surface);
155 outline: none;
156 padding-bottom: 0.4375rem;
157 border-bottom-width: 0.125rem;
158 border-bottom-color: var(--md-sys-color-primary);
159}
160
161.md-filled-text-field:hover :focus {
162 padding-bottom: 0.4375rem;
163 border-bottom-width: 0.125rem;
164}
165
166.md-filled-text-field:focus-within small {
167 color: var(--md-sys-color-on-surface-variant);
168}
169
170.md-filled-text-field :invalid {
171 color: var(--md-sys-color-on-surface);
172 border-bottom-color: var(--md-sys-color-error);
173}
174
175.md-filled-text-field :invalid+span,
176.md-filled-text-field :invalid+label {
177 color: var(--md-sys-color-error);
178}
179
180.md-filled-text-field :invalid~small,
181.md-filled-text-field:hover :invalid~small,
182.md-filled-text-field:focus-within .input-text:invalid~small {
183 color: var(--md-sys-color-error);
184}
185
186.md-filled-text-field :invalid:focus {
187 caret-color: var(--md-sys-color-error);
188 border-bottom-color: var(--md-sys-color-error);
189}
190
191.md-filled-text-field:hover :invalid {
192 color: var(--md-sys-color-on-surface);
193 border-bottom-color: var(--md-sys-color-error);
194}

6. lib / css / 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}

7. lib / css / 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}

8. lib / css / md-navigation-bar.css

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

9. lib / css / 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}

10. lib / css / md-ripple.css

1@keyframes md-ripple {
2
3 from {
4 background-size: 100%;
5 }
6
7 to {
8 background-size: 15000%;
9 }
10
11}

11. lib / css / md-segmented-button.css

1.md-segmented-button {
2 display: flex;
3 align-items: stretch;
4 box-sizing: border-box;
5 border: 0.0625rem solid var(--md-sys-color-outline);
6 height: 2.5rem;
7 border-radius: 1.25rem;
8 overflow: hidden;
9}
10
11.md-segmented-button[hidden] {
12 display: none;
13}
14
15.md-segmented-button input {
16 -webkit-appearance: none;
17 appearance: none;
18 flex: 0 1 0.0625rem;
19 width: 0.0625rem;
20 height: 2.375rem;
21 margin: 0;
22 padding: 0;
23 background-color: var(--md-sys-color-outline);
24}
25
26.md-segmented-button input:first-of-type {
27 transform: scaleX(0);
28}
29
30.md-segmented-button input:focus {
31 outline: none;
32}
33
34.md-segmented-button :checked+label {
35 color: var(--md-sys-color-on-secondary-container);
36}
37
38.md-segmented-button label {
39 position: relative;
40 flex: 1 1 1.5rem;
41 display: block;
42 box-sizing: border-box;
43 height: 2.375rem;
44 line-height: 2.375rem;
45 text-align: center;
46 color: var(--md-sys-color-on-surface);
47 font-family: var(--md-sys-typescale-label-large-font);
48 font-weight: var(--md-sys-typescale-label-large-weight);
49 font-size: var(--md-sys-typescale-label-large-size);
50 font-style: var(--md-sys-typescale-label-large-font-style);
51 letter-spacing: var(--md-sys-typescale-label-large-tracking);
52 text-transform: var(--md-sys-typescale-label-large-text-transform);
53 text-decoration: var(--md-sys-typescale-label-large-text-decoration);
54 padding: 0 0.75rem;
55 overflow: hidden;
56 white-space: nowrap;
57 text-overflow: ellipsis;
58 overflow: hidden;
59}
60
61.md-segmented-button label::before {
62 /* container */
63 content: "";
64 position: absolute;
65 z-index: -2;
66 top: 0;
67 right: 0;
68 left: 0;
69 bottom: 0;
70}
71
72.md-segmented-button label::after {
73 /* state layer */
74 content: "";
75 position: absolute;
76 z-index: -1;
77 top: 0;
78 right: 0;
79 left: 0;
80 bottom: 0;
81 background-color: transparent;
82}
83
84.md-segmented-button :checked+label::before {
85 /* container */
86 background-color: var(--md-sys-color-secondary-container);
87}
88
89.md-segmented-button label span {
90 vertical-align: middle;
91 color: var(--md-sys-color-on-surface);
92 font-size: 1.125rem;
93 width: 1.125rem;
94 height: 1.125rem;
95 margin-right: 0.5rem;
96}
97
98
99.md-segmented-button label span:first-child {
100 display: none;
101}
102
103.md-segmented-button :checked+label span:first-child {
104 display: inline-block;
105 color: var(--md-sys-color-on-secondary-container);
106}
107
108/* state layer */
109.md-segmented-button label:hover::after {
110 background-color: var(--md-sys-color-on-surface);
111 opacity: var(--md-sys-state-hover-state-layer-opacity);
112}
113
114/* state layer */
115.md-segmented-button :checked+label:hover::after {
116 background-color: var(--md-sys-color-on-secondary-container);
117}
118
119/* state layer */
120.md-segmented-button :focus+label::after {
121 background-color: var(--md-sys-color-on-surface);
122 opacity: var(--md-sys-state-focus-state-layer-opacity);
123}
124
125/* state layer */
126.md-segmented-button :focus:checked+label::after {
127 background-color: var(--md-sys-color-on-secondary-container);
128}
129
130.md-segmented-button label:active,
131.md-segmented-button :active+label {
132 background-position: center;
133 background-image:
134 radial-gradient(circle, var(--md-riple-color) 1%, transparent 1%);
135 background-size: 100%;
136 animation-name: md-ripple;
137 animation-duration: var(--md-sys-motion-duration-500);
138}
139
140/* state layer */
141.md-segmented-button :active+label::after {
142 background-color: var(--md-sys-color-on-surface);
143 opacity: var(--md-sys-state-pressed-state-layer-opacity);
144}
145
146/* state layer */
147.md-segmented-button :active:checked+label::after {
148 background-color: var(--md-sys-color-on-secondary-container);
149}

12. lib / css / md-slider-field.css

1md-slider-field input::-webkit-slider-runnable-track {
2 height: 0.25rem;
3 border-radius: 0.125rem;
4}
5
6md-slider-field input::-webkit-slider-thumb {
7 -webkit-appearance: none;
8 background-color: var(--md-sys-color-primary);
9 width: 1.25rem;
10 height: 1.25rem;
11 border-radius: 0.625rem;
12 margin-top: -0.5625rem;
13}
14
15md-slider-field input:hover::-webkit-slider-thumb {
16 box-shadow: 0 0 0 0.625rem color-mix(in srgb, var(--md-sys-color-primary), transparent var(--state-hover-transparency-percentage));
17}
18
19md-slider-field input:focus::-webkit-slider-thumb {
20 box-shadow: 0 0 0 0.625rem color-mix(in srgb, var(--md-sys-color-primary), transparent var(--state-focus-transparency-percentage));
21}
22
23md-slider-field input:active::-webkit-slider-thumb {
24 box-shadow: 0 0 0 0.625rem color-mix(in srgb, var(--md-sys-color-primary), transparent var(--state-pressed-transparency-percentage)) !important;
25 background-position: center;
26 background-image:
27 radial-gradient(circle, var(--md-sys-color-primary-container) 1%, transparent 1%);
28 background-size: 100%;
29 animation-name: md-ripple;
30 animation-duration: var(--md-sys-motion-duration-500);
31}
32
33md-slider-field.material::-moz-range-track {
34 height: 0.25rem;
35 border-radius: 0.125rem;
36}
37
38md-slider-field input::-moz-range-thumb {
39 -webkit-appearance: none;
40 appearance: none;
41 background-color: var(--md-sys-color-primary);
42 width: 1.25rem;
43 height: 1.25rem;
44 border: none;
45 border-radius: 0.625rem;
46}
47
48md-slider-field input:hover::-moz-range-thumb {
49 box-shadow: 0 0 0 0.625rem color-mix(in srgb, var(--md-sys-color-primary), transparent var(--state-hover-transparency-percentage));
50}
51
52md-slider-field input:focus::-moz-range-thumb {
53 box-shadow: 0 0 0 0.625rem color-mix(in srgb, var(--md-sys-color-primary), transparent var(--state-focus-transparency-percentage));
54}
55
56md-slider-field input:active::-moz-range-thumb {
57 box-shadow: 0 0 0 0.625rem color-mix(in srgb, var(--md-sys-color-primary), transparent var(--state-pressed-transparency-percentage)) !important;
58 background-position: center;
59 background-image:
60 radial-gradient(circle, var(--md-sys-color-primary-container) 1%, transparent 1%);
61 background-size: 100%;
62 animation-name: md-ripple;
63 animation-duration: var(--md-sys-motion-duration-500);
64}

13. lib / css / md-standard-icon-button.css

1.md-standard-icon-button {
2 position: relative;
3 display: inline-block;
4 border: none;
5 padding: 0.25rem;
6 background-color: transparent;
7 text-decoration: none;
8 border-radius: 50%;
9 overflow: hidden;
10}
11
12.md-standard-icon-button[hidden] {
13 display: none;
14}
15
16/* state layer */
17.md-standard-icon-button::after {
18 content: "";
19 position: absolute;
20 top: 0.25rem;
21 right: 0.25rem;
22 left: 0.25rem;
23 bottom: 0.25rem;
24 border-radius: 50%;
25}
26
27.md-standard-icon-button span {
28 position: relative;
29 padding: 0.5rem;
30 color: var(--md-sys-color-on-surface-variant);
31 font-size: 1.5rem;
32 width: 1.5rem;
33 height: 1.5rem;
34}
35
36.md-standard-icon-button.avatar span {
37 padding: 0.3125rem;
38 font-size: 1.875rem;
39 width: 1.875rem;
40 height: 1.875rem;
41}
42
43.md-standard-icon-button:hover::after {
44 background-color: var(--md-sys-color-on-surface-variant);
45 opacity: var(--md-sys-state-hover-state-layer-opacity);
46}
47
48.md-standard-icon-button:hover span {
49 color: var(--md-sys-color-on-surface-variant);
50}
51
52.md-standard-icon-button:focus {
53 outline: none;
54}
55
56.md-standard-icon-button:focus::after {
57 background-color: var(--md-sys-color-on-surface-variant);
58 opacity: var(--md-sys-state-focus-state-layer-opacity);
59}
60
61.md-standard-icon-button:focus span {
62 color: var(--md-sys-color-on-surface-variant);
63}
64
65.md-standard-icon-button:active {
66 background-position: center;
67 background-image:
68 radial-gradient(circle, var(--md-riple-color) 1%, transparent 1%);
69 background-size: 100%;
70 animation-name: md-ripple;
71 animation-duration: var(--md-sys-motion-duration-500);
72}
73
74.md-standard-icon-button:active::after {
75 background-color: var(--md-sys-color-on-surface-variant);
76 opacity: var(--md-sys-state-pressed-state-layer-opacity);
77}
78
79.md-standard-icon-button:active span {
80 -color: var(--md-sys-color-on-surface-variant);
81}
82
83.md-standard-icon-button:disabled::after {
84 background-color: transparent !important;
85 opacity: 1;
86}
87
88.md-standard-icon-button:disabled span {
89 color: var(--md-sys-color-on-surface) !important;
90 opacity: 0.38;
91}
92
93.md-standard-icon-button:disabled:active {
94 background-image: none;
95 animation-name: none;
96 animation-duration: unset;
97}

14. lib / css / md-switch.css

1.md-switch {
2 -webkit-appearance: none;
3 appearance: none;
4 position: relative;
5 display: inline-block;
6 vertical-align: middle;
7 box-sizing: content-box;
8 padding: 0;
9 padding-block: 0;
10 padding-inline: 0;
11}
12
13.md-switch:focus {
14 outline: none;
15}
16
17/* Track */
18body.material .md-switch {
19 width: 3rem;
20 height: 1.75rem;
21 border-radius: 1rem;
22 border: 0.125rem solid var(--md-sys-color-outline);
23 background-color: var(--md-sys-color-surface-container-highest);
24}
25
26body.material .md-switch:checked {
27 background-color: var(--md-sys-color-primary);
28}
29
30/* State */
31body.material .md-switch::before {
32 content: "";
33 display: none;
34 position: absolute;
35 height: 2.5rem;
36 width: 2.5rem;
37 border-radius: 1.25rem;
38 top: -0.375rem;
39 left: -0.375rem;
40}
41
42body.material .md-switch:checked:before {
43 left: auto;
44 right: -0.375rem;
45}
46
47/* Handle */
48body.material .md-switch::after {
49 content: "";
50 display: inline-block;
51 position: absolute;
52 transition-property: all;
53 transition-duration: var(--md-sys-motion-duration-700);
54 height: 1rem;
55 width: 1rem;
56 border-radius: 0.5rem;
57 top: 0.375rem;
58 left: 0.375rem;
59 background-color: var(--md-sys-color-outline);
60 box-shadow: var(--md-box_shadow_level1);
61}
62
63body.material .md-switch:checked:after {
64 height: 1.5rem;
65 width: 1.5rem;
66 border-radius: 0.75rem;
67 top: 0.125rem;
68 left: auto;
69 right: 0.125rem;
70 background-color: var(--md-sys-color-on-primary);
71}
72
73body.material .md-switch:hover::before {
74 display: inline-block;
75 background-color: var(--md-sys-color-on-surface);
76 opacity: var(--md-sys-state-hover-state-layer-opacity);
77}
78
79body.material .md-switch:checked:hover::before {
80 background-color: var(--md-sys-color-primary);
81}
82
83body.material .md-switch:hover::after {
84 background-color: var(--md-sys-color-on-surface-variant);
85}
86
87body.material .md-switch:checked:hover::after {
88 background-color: var(--md-sys-color-primary-container);
89}
90
91body.material .md-switch:focus::before {
92 display: inline-block;
93 background-color: var(--md-sys-color-on-surface);
94 opacity: var(--md-sys-state-focus-state-layer-opacity);
95}
96
97body.material .md-switch:checked:focus::before {
98 background-color: var(--md-sys-color-primary);
99}
100
101body.material .md-switch:focus::after {
102 background-color: var(--md-sys-color-on-surface-variant);
103}
104
105body.material .md-switch:checked:focus::after {
106 background-color: var(--md-sys-color-primary-container);
107}
108
109body.material .md-switch:active::before {
110 display: inline-block;
111 background-color: var(--md-sys-color-on-surface);
112 opacity: var(--md-sys-state-focus-state-layer-opacity);
113}
114
115
116body.material .md-switch:checked:active::before {
117 background-color: var(--md-sys-color-primary);
118}
119
120body.material .md-switch:active::after {
121 width: 1.75rem;
122 height: 1.75rem;
123 top: 0;
124 left: 0;
125 border-radius: 0.875rem;
126 background-position: center;
127 animation-name: md-ripple;
128 animation-duration: var(--md-sys-motion-duration-500);
129 background-size: 100%;
130 background-color: var(--md-sys-color-on-surface-variant);
131 background-image:
132 radial-gradient(circle, var(--md-sys-color-primary-container) 1%, transparent 1%);
133 box-shadow: var(--md-box_shadow_level1), 0 0 0 0.375rem color-mix(in srgb, var(--md-sys-color-on-surface), transparent var(--state-pressed-transparency-percentage)) !important;
134}
135
136body.material .md-switch:checked:active::after {
137 left: auto;
138 right: 0;
139 background-color: var(--md-sys-color-primary-container);
140 background-image:
141 radial-gradient(circle, var(--md-riple-color) 1%, transparent 1%);
142 box-shadow: var(--md-box_shadow_level1), 0 0 0 0.375rem color-mix(in srgb, var(--md-sys-color-primary), transparent var(--state-pressed-transparency-percentage)) !important;
143}
144
145body.apple .md-switch {
146 width: 3rem;
147 border-radius: 0.875rem;
148 height: 1.75rem;
149 background-color: var(--colIntIosOffBk);
150}
151
152body.apple .md-switch:checked {
153 background-color: var(--colIntIosOnBk);
154}
155
156body.apple .md-switch:focus {
157 background-color: var(--colIntIosOffBkFc);
158}
159
160body.apple .md-switch:checked:focus {
161 background-color: var(--colIntIosOnBkFc);
162}
163
164body.apple .md-switch::after {
165 content: "";
166 display: inline-block;
167 position: absolute;
168 width: 1.5rem;
169 height: 1.5rem;
170 border-radius: 0.75rem;
171 top: 0.125rem;
172 left: 0.125rem;
173 background-color: var(--colIntIos);
174}
175
176body.apple .md-switch:checked::after {
177 left: auto;
178 right: 0.125rem;
179 border-color: var(--colIntIosOnBk);
180}
181
182body.apple .md-switch:focus::after {
183 border-color: var(--colIntIosOffBkFc);
184}
185
186body.apple .md-switch:checked:focus::after {
187 border-color: var(--colIntIosOnBkFc);
188}

15. lib / css / 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}
78
79.md-tab a:hover {
80 color: var(--md-sys-color-on-surface);
81}
82
83/* state layer */
84.md-tab a:hover::after {
85 background-color: var(--md-sys-color-on-surface);
86 opacity: var(--md-sys-state-hover-state-layer-opacity);
87}
88
89.md-tab a.active:hover {
90 color: var(--md-sys-color-primary);
91}
92
93/* state layer */
94.md-tab a.active:hover::after {
95 background-color: var(--md-sys-color-primary);
96 opacity: var(--md-sys-state-hover-state-layer-opacity);
97}
98
99.md-tab a:hover span {
100 color: var(--md-sys-color-on-surface);
101}
102
103.md-tab a.active:hover span {
104 color: var(--md-sys-color-primary);
105}
106
107.md-tab a:focus {
108 outline: none;
109}
110
111/* state layer */
112.md-tab a:focus::after {
113 background-color: var(--md-sys-color-on-surface);
114 opacity: var(--md-sys-state-focus-state-layer-opacity);
115}
116
117/* state layer */
118.md-tab a.active:focus::after {
119 background-color: var(--md-sys-color-primary);
120 opacity: var(--md-sys-state-hover-state-layer-opacity);
121}
122
123.md-tab a:active {
124 background-position: center;
125 background-image:
126 radial-gradient(circle, var(--md-riple-color) 1%, transparent 1%);
127 background-size: 100%;
128 animation-name: md-ripple;
129 animation-duration: var(--md-sys-motion-duration-500);
130}
131
132/* state layer */
133.md-tab a:active::after {
134 background-color: var(--md-sys-color-on-surface);
135 opacity: var(--md-sys-state-pressed-state-layer-opacity);
136}
137
138/* state layer */
139.md-tab a.active:active::after {
140 background-color: var(--md-sys-color-primary);
141 opacity: var(--md-sys-state-pressed-state-layer-opacity);
142}

16. lib / css / md-top-app-bar.css

1.md-headline {
2 box-sizing: border-box;
3 margin: 0;
4 color: var(--md-sys-color-on-surface);
5 background-color: var(--md-sys-color-surface);
6 transition-property: color;
7 transition-duration: var(--md-sys-motion-duration-1000);
8 transition-timing-function: ease-in;
9}
10
11.md-headline.scroll-adicional {
12 color: var(--md-sys-color-surface-container-low);
13 background-color: var(--md-sys-color-surface-container-low);
14}
15
16.md-headline.scroll {
17 color: var(--md-sys-color-surface);
18}
19
20.md-headline.headline-small {
21 padding: 0 1rem 1.5rem 1rem;
22}
23
24.md-headline.headline-medium {
25 padding: 0 1rem 1.75rem 1rem;
26}
27
28md-top-app-bar[headline] h1 {
29 opacity: 0;
30 transition-property: opacity;
31 transition-duration: var(--md-sys-motion-duration-1000);
32}
33
34md-top-app-bar[headline].scroll h1 {
35 opacity: 1;
36 transition-timing-function: ease-in;
37}

17. lib / css / roboto.css

1/* roboto-regular - latin */
2@font-face {
3 /* Revisa
4 * https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display
5 * para otras opciones. */
6 font-display: swap;
7 font-family: 'Roboto';
8 font-style: normal;
9 font-weight: 400;
10 /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
11 src: url('../fonts/roboto-v32-latin-regular.woff2') format('woff2');
12}

B. Carpeta « lib / fonts »

Versión para imprimir.

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

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

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

4. lib / fonts / roboto-v32-latin-regular.woff2

C. Carpeta « lib / js »

Versión para imprimir.

1. lib / js / abreElementoHtml.js

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

2. lib / js / cierraElementoHtmo.js

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

3. lib / js / exportaAHtml.js

1/**
2 * Permite que los eventos de html usen la función.
3 * @param {function} functionInstance
4 */
5export function exportaAHtml(functionInstance) {
6 window[nombreDeFuncionParaHtml(functionInstance)] = functionInstance
7}
8
9/**
10 * @param {function} valor
11 */
12export function nombreDeFuncionParaHtml(valor) {
13 const names = valor.name.split(/\s+/g)
14 return names[names.length - 1]
15}

4. lib / js / getAttribute.js

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

5. lib / js / htmlentities.js

1/**
2 * Codifica un texto para que cambie los caracteres
3 * especiales y no se pueda interpretar como
4 * etiiqueta HTML. Esta técnica evita la inyección
5 * de código.
6 * @param { string } texto
7 * @returns { string } un texto que no puede
8 * interpretarse como HTML. */
9export function htmlentities(texto) {
10 return texto.replace(/[<>"']/g, textoDetectado => {
11 switch (textoDetectado) {
12 case "<": return "<"
13 case ">": return ">"
14 case '"': return """
15 case "'": return "'"
16 default: return textoDetectado
17 }
18 })
19}

6. lib / js / muestraError.js

1import { exportaAHtml } from "./exportaAHtml.js"
2import { ProblemDetails } from "./ProblemDetails.js"
3
4/**
5 * Muestra un error en la consola y en un cuadro de
6 * alerta el mensaje de una excepción.
7 * @param { ProblemDetails | Error | null } error descripción del error.
8 */
9export function muestraError(error) {
10
11 if (error === null) {
12
13 console.error("Error")
14 alert("Error")
15
16 } else if (error instanceof ProblemDetails) {
17
18 let mensaje = error.title
19 if (error.detail) {
20 mensaje += `\n\n${error.detail}`
21 }
22 mensaje += `\n\nCódigo: ${error.status}`
23 if (error.type) {
24 mensaje += ` ${error.type}`
25 }
26
27 console.error(mensaje)
28 console.error(error)
29 console.error("Headers:")
30 error.headers.forEach((valor, llave) => console.error(llave, "=", valor))
31 alert(mensaje)
32
33 } else {
34
35 console.error(error)
36 alert(error.message)
37
38 }
39
40}
41
42exportaAHtml(muestraError)

7. lib / js / muestraTextoDeAyuda.js

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

8. lib / js / ProblemDetails.js

1/**
2 * Detalle de los errores devueltos por un servicio.
3 */
4export class ProblemDetails extends Error {
5
6 /**
7 * @param {number} status
8 * @param {Headers} headers
9 * @param {string} title
10 * @param {string} [type]
11 * @param {string} [detail]
12 */
13 constructor(status, headers, title, type, detail) {
14 super(title)
15 /**
16 * @readonly
17 */
18 this.status = status
19 /**
20 * @readonly
21 */
22 this.headers = headers
23 /**
24 * @readonly
25 */
26 this.type = type
27 /**
28 * @readonly
29 */
30 this.detail = detail
31 /**
32 * @readonly
33 */
34 this.title = title
35 }
36
37}

9. lib / js / querySelector.js

1/**
2 * @template { HTMLElement } T
3 * @param { Document | Element | ShadowRoot } raiz
4 * @param { string } query
5 * @returns { T }
6 */
7export 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. lib / js / resaltaSiEstasEn.js

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

11. Carpeta « lib / js / const »

A. lib / js / const / ES_APPLE.js

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

12. Carpeta « lib / js / custom »

A. lib / js / custom / md-menu-button.js

1import { MdNavigationDrawer } from "./MdNavigationDrawer.js"
2
3export class MdMenuButton extends HTMLButtonElement {
4
5 constructor() {
6 super()
7 this.abreDrawer = this.abreDrawer.bind(this)
8 }
9
10 connectedCallback() {
11 this.type = "button"
12 this.classList.add("md-standard-icon-button")
13 this.innerHTML = /* HTML */
14 `<span class="material-symbols-outlined">menu</span>`
15 this.addEventListener("click", this.abreDrawer)
16 }
17
18 disconnectedCallback() {
19 this.removeEventListener("click", this.abreDrawer)
20 }
21
22 abreDrawer() {
23 const drawer = document.querySelector(".drawer")
24 if (drawer instanceof MdNavigationDrawer) {
25 drawer.abre()
26 }
27 }
28}
29
30customElements.define("md-menu-button", MdMenuButton, { extends: "button" })

B. lib / js / custom / md-options-menu.js

1import { abreElementoHtml } from "../abreElementoHtml.js"
2import { cierraElementoHtmo } from "../cierraElementoHtmo.js"
3import { querySelector } from "../querySelector.js"
4
5export 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
105customElements.define("md-options-menu", MdOptionsMenu)

C. lib / js / custom / md-overflow-button.js

1import { ES_APPLE } from "../const/ES_APPLE.js"
2
3export class MdOverflowButton extends HTMLButtonElement {
4
5 connectedCallback() {
6 this.type = "button"
7 this.classList.add("md-standard-icon-button")
8 this.innerHTML = ES_APPLE
9 ? /* HTML */
10 `<span style="color: var(--md-sys-color-on-surface-variant)"
11 class="material-symbols-outlined">
12 more_horiz
13 </span>`
14 : /* HTML */
15 `<span style="color: var(--md-sys-color-on-surface-variant)"
16 class="material-symbols-outlined">
17 more_vert
18 </span>`
19 }
20
21}
22
23customElements
24 .define("md-overflow-button", MdOverflowButton, { extends: "button" })

D. lib / js / custom / md-overflow-menu.js

1import { abreElementoHtml } from "../abreElementoHtml.js"
2import { cierraElementoHtmo } from "../cierraElementoHtmo.js"
3
4export class MdOverflowMenu extends HTMLElement {
5
6 getContent() {
7 return /* HTML */`
8
9 <style>
10
11 :host {
12 position: fixed;
13 min-width: 7rem;
14 max-width: 280px;
15 }
16
17 ::slotted(*) {
18 text-align: start;
19 width: 100%;
20 border: none;
21 background-color: transparent;
22 }
23
24 </style>
25
26 <slot></slot>`
27 }
28
29 constructor() {
30 super()
31 const shadow = this.attachShadow({ mode: "open" })
32 shadow.innerHTML = this.getContent()
33 this.clicCierra = this.clicCierra.bind(this)
34 /**
35 * @private
36 * @type {HTMLButtonElement| null}
37 */
38 this._toggleButton = null
39 }
40
41 connectedCallback() {
42 this.classList.add("md-menu")
43 this.role = "menu"
44 }
45
46 /**
47 * @param {HTMLButtonElement} toggleButton
48 */
49 alterna(toggleButton) {
50 this._toggleButton = toggleButton
51 const top = toggleButton.offsetTop + toggleButton.offsetHeight - 4
52 const right =
53 innerWidth - (toggleButton.offsetLeft + toggleButton.offsetWidth) - 3
54 this.style.top = `${top}px`
55 this.style.right = `${right}px`
56 const list = this.classList
57 if (list.contains("open")) {
58 this.cierra()
59 } else {
60 this.abre()
61 }
62 }
63
64 abre() {
65 document.addEventListener("click", this.clicCierra)
66 abreElementoHtml(this)
67 }
68
69 cierra() {
70 document.removeEventListener("click", this.clicCierra)
71 cierraElementoHtmo(this)
72 }
73
74 /**
75 * @param {Event} evt
76 */
77 clicCierra(evt) {
78 const target = evt.target
79 if (this.classList.contains("open")
80 && this._toggleButton !== null
81 && target instanceof HTMLElement
82 && !this._toggleButton.contains(target)) {
83 this.cierra()
84 }
85 }
86}
87
88customElements.define("md-overflow-menu", MdOverflowMenu)

E. lib / js / custom / md-select-menu.js

1import { getAttribute } from "../getAttribute.js"
2import { querySelector } from "../querySelector.js"
3import { MdOptionsMenu } from "./md-options-menu.js"
4
5export 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="/lib/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 parentElement = this.parentElement
262 if (parentElement !== null) {
263 const optionsMenu = this.optionsMenu
264 if (optionsMenu !== null) {
265 optionsMenu.style.top = `${parentElement.offsetTop + 58}px`
266 optionsMenu.style.left = `${parentElement.offsetLeft}px`
267 optionsMenu.style.width = `${parentElement.offsetWidth}px`
268 optionsMenu.abre()
269 this.focus()
270 optionsMenu.addEventListener("click", this._clicEnDialogo)
271 }
272 this.ariaExpanded = "true"
273 document.addEventListener("click", this.clicExterno)
274 }
275 }
276
277 /** @private */
278 _cierra() {
279 this.classList.remove("open")
280 const optionsMenu = this.optionsMenu
281 if (optionsMenu !== null) {
282 optionsMenu.cierra()
283 optionsMenu.removeEventListener("click", this._clicEnDialogo)
284 }
285 this.ariaExpanded = "false"
286 document.removeEventListener("click", this.clicExterno)
287 this.dispatchEvent(new Event("input", { bubbles: true }))
288 }
289
290 get optionsMenu() {
291 if (this._optionsMenu === null) {
292 if (this.options !== "") {
293 const optionsMenu = document.getElementById(this.options)
294 if (optionsMenu instanceof MdOptionsMenu) {
295 this._optionsMenu = optionsMenu
296 } else {
297 throw new Error(`Valor incorrecto para options: "${this.options}".`)
298 }
299 }
300 }
301 return this._optionsMenu
302 }
303
304 /** @private */
305 _avanzaOpcion() {
306 const i = this._valueIndex
307 if (i > -1) {
308 const optionsMenu = this.optionsMenu
309 if (optionsMenu !== null) {
310 const opciones = optionsMenu.opciones
311 if (i < opciones.length - 1) {
312 this.value = getAttribute(opciones[i + 1], "data-value")
313 }
314 }
315 }
316 }
317
318 /** @private */
319 _retrocedeOpcion() {
320 const i = this._valueIndex
321 if (i > -1) {
322 const optionsMenu = this.optionsMenu
323 if (optionsMenu !== null) {
324 const opciones = optionsMenu.opciones
325 if (i > 0) {
326 this.value = getAttribute(opciones[i - 1], "data-value")
327 }
328 }
329 }
330 }
331
332 /**
333 * @private
334 * @returns {number}
335 */
336 get _valueIndex() {
337 const value = this.value
338 const optionsMenu = this.optionsMenu
339 return (optionsMenu === null
340 ? -1
341 : optionsMenu.opciones.findIndex(opcion => opcion.dataset.value === value))
342 }
343
344 /**
345 * @private
346 * @param {Event} event
347 */
348 _clicEnDialogo(event) {
349 const target = event.target
350 const optionsMenu = this.optionsMenu
351 let value = ""
352 if (optionsMenu !== null) {
353 for (const opcion of optionsMenu.opciones) {
354 if (opcion === target) {
355 opcion.classList.add("selected")
356 value = getAttribute(opcion, "data-value")
357 } else {
358 opcion.classList.remove("selected")
359 }
360 }
361 }
362 this.value = value
363 this._cierra()
364 this.focus()
365}
366
367 /**
368 * @param {Event} evt
369 */
370 clicExterno(evt) {
371 const target = evt.target
372 const parentElement = this.parentElement
373 const optionsMenu = this._optionsMenu
374 if (this.classList.contains("open")
375 && target instanceof HTMLElement
376 && parentElement !== null
377 && !parentElement.contains(target)
378 && optionsMenu !== null
379 && !optionsMenu.contains(target)) {
380 this._cierra()
381 }
382 }
383
384 /**
385 * @param { KeyboardEvent } event
386 */
387 _onKeyDown(event) {
388 const key = event.key
389 const optionsMenu = this._optionsMenu
390 if (optionsMenu !== null) {
391 if (optionsMenu.classList.contains("open")) {
392 if (key === "ArrowDown") {
393 event.preventDefault()
394 this._avanzaOpcion()
395 } else if (key === "ArrowUp") {
396 event.preventDefault()
397 this._retrocedeOpcion()
398 } else if (key === "Escape") {
399 event.preventDefault()
400 this._cierra()
401 } else if (key === " ") {
402 event.preventDefault()
403 this._cierra()
404 } else if (key === "Tab") {
405 this._cierra()
406 } else {
407 event.preventDefault()
408 }
409 } else if (key === " ") {
410 event.preventDefault()
411 this._abre()
412 } else if (key === "Tab") {
413 this._cierra()
414 } else {
415 event.preventDefault()
416 }
417 }
418 }
419
420}
421
422MdSelectMenu.formAssociated = true
423
424customElements.define("md-select-menu", MdSelectMenu)

F. lib / js / custom / md-slider-field.js

1import { querySelector } from "../querySelector.js"
2
3export class MdSliderField extends HTMLElement {
4
5 getContent() {
6 return /* HTML */`
7 <style>
8 :host {
9 display: block;
10 margin: 1rem;
11 }
12
13 :host([hidden]) {
14 display: none;
15 }
16
17 #label::slotted(*) {
18 display: block;
19 white-space: nowrap;
20 text-overflow: ellipsis;
21 overflow: hidden;
22 color: var(--md-sys-color-on-surface-variant);
23 font-family: var(--md-sys-typescale-body-small-font);
24 font-weight: var(--md-sys-typescale-body-small-weight);
25 font-size: var(--md-sys-typescale-body-small-size);
26 font-style: var(--md-sys-typescale-body-small-font-style);
27 letter-spacing: var(--md-sys-typescale-body-small-tracking);
28 line-height: var(--md-sys-typescale-body-small-line-height);
29 text-transform: var(--md-sys-typescale-body-small-text-transform);
30 text-decoration: var(--md-sys-typescale-body-small-text-decoration);
31 }
32
33 [name="slider"]::slotted(input) {
34 -webkit-appearance: none;
35 appearance: none;
36 height: 0.25rem;
37 border-radius: 0.125rem;
38 background-image:
39 linear-gradient(to right, var(--md-sys-color-primary) 0%, var(--md-sys-color-primary) 50%, var(--md-sys-color-surface-container-highest) 50%, var(--md-sys-color-surface-container-highest) 100%);
40 }
41
42 [name="slider"]::slotted(input:focus) {
43 outline: none;
44 }
45
46 [name="supporting"]::slotted(*) {
47 display: block;
48 color: var(--md-sys-color-on-surface-variant);
49 font-family: var(--md-sys-typescale-body-small-font);
50 font-weight: var(--md-sys-typescale-body-small-weight);
51 font-size: var(--md-sys-typescale-body-small-size);
52 font-style: var(--md-sys-typescale-body-small-font-style);
53 letter-spacing: var(--md-sys-typescale-body-small-tracking);
54 line-height: var(--md-sys-typescale-body-small-line-height);
55 text-transform: var(--md-sys-typescale-body-small-text-transform);
56 text-decoration: var(--md-sys-typescale-body-small-text-decoration);
57 padding-top: 0.5rem;
58 white-space: nowrap;
59 text-overflow: ellipsis;
60 overflow: hidden;
61 }
62 </style>
63 <slot id="label"></slot>
64 <slot name="slider"></slot>
65 <slot name="supporting"></slot>`
66 }
67
68 constructor() {
69 super()
70 const shadow = this.attachShadow({ mode: "open", delegatesFocus: true })
71 shadow.innerHTML = this.getContent()
72 this._configuraSlider = this._configuraSlider.bind(this)
73 this.analiza = this.analiza.bind(this)
74
75 /**
76 * @private
77 * @type {HTMLSlotElement}
78 */
79 this._slotSlider = querySelector(shadow, '[name="slider"]')
80 /**
81 * @private
82 * @type {HTMLInputElement|null}
83 */
84 this._input = null
85 this._slotSlider.addEventListener("slotchange", this._configuraSlider)
86 }
87
88 /** @private */
89 _configuraSlider() {
90 if (this._input !== null) {
91 this._input.removeEventListener("input", this.analiza)
92 this._input = null
93 }
94 for (const input of this._slotSlider.assignedElements()) {
95 if (input instanceof HTMLInputElement) {
96 this._input = input
97 input.addEventListener("input", this.analiza)
98 this.analiza()
99 }
100 }
101 }
102
103 analiza() {
104 const i = this._input
105 if (i !== null) {
106 const v = i.valueAsNumber
107 const min = parseFloat(i.min)
108 const max = parseFloat(i.max)
109 const value = (v - min) / (max - min) * 100
110 i.title = v.toString()
111 i.style.background =
112 `linear-gradient(to right, var(--md-sys-color-primary) 0%, var(--md-sys-color-primary) ${value
113 }%, var(--md-sys-color-surface-container-highest) ${value
114 }%, var(--md-sys-color-surface-container-highest) 100%)`
115 }
116 }
117
118}
119
120customElements.define("md-slider-field", MdSliderField)

G. lib / js / custom / md-top-app-bar.js

1import { ES_APPLE } from "../const/ES_APPLE.js"
2import { getAttribute } from "../getAttribute.js"
3import { querySelector } from "../querySelector.js"
4
5class MdTopAppBar 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(.centrado) #acciones,
49 :host(.center-aligned) #acciones {
50 flex: 0 0 3rem;
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(.center-aligned) #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 idHeadline = getAttribute(this, "headline")
146 if (idHeadline !== "") {
147 const headline = document.getElementById(idHeadline)
148 if (headline instanceof HTMLHeadingElement) {
149 this._headline = headline
150 if (this.classList.contains("apple") || this.classList.contains("medium")) {
151 headline.classList.add("md-headline", "headline-small")
152 } else {
153 headline.classList.add("md-headline", "headline-medium")
154 }
155 }
156 }
157 const idAdicional = getAttribute(this, "adicional")
158 if (idAdicional !== "") {
159 this._adicional = document.getElementById(idAdicional)
160 if (this._adicional !== null) {
161 if (this.classList.contains("apple")) {
162 this._adicional.style.top = "env(titlebar-area-height, 3rem)"
163 } else {
164 this._adicional.style.top = "env(titlebar-area-height, 4rem)"
165 }
166 }
167 }
168 }
169
170 _configuraAction() {
171 const assignedElements = this._action.assignedElements()
172 if (this.isConnected) {
173 if (ES_APPLE) {
174 this.classList.add("apple")
175 this.classList.remove("material")
176 } else {
177 this.classList.add("material")
178 this.classList.remove("apple")
179 }
180 if (this.classList.contains("center-aligned")) {
181 this.classList.remove("centrado")
182 this.classList.remove("justificado")
183 } else {
184 if (ES_APPLE && assignedElements.length <= 1) {
185 this.classList.add("centrado")
186 this.classList.remove("justificado")
187 } else {
188 this.classList.add("justificado")
189 this.classList.remove("centrado")
190 }
191 }
192 }
193 }
194
195 /** @private */
196 _onScroll() {
197 this._posY = scrollY
198 if (!this._scrolling) {
199 requestAnimationFrame(() => this._avanza())
200 }
201 this._scrolling = true
202 }
203
204 /** @private */
205 _avanza() {
206 if (this._posY === 0) {
207 this.classList.remove("scroll")
208 if (this._headline !== null) {
209 if (this._adicional === null) {
210 this._headline.classList.remove("scroll")
211 } else {
212 this._headline.classList.remove("scroll-adicional")
213 }
214 }
215 if (this._adicional !== null) {
216 this._adicional.classList.remove("scroll")
217 }
218 } else {
219 this.classList.add("scroll")
220 if (this._headline !== null) {
221 if (this._adicional === null) {
222 this._headline.classList.add("scroll")
223 } else {
224 this._headline.classList.add("scroll-adicional")
225 }
226 }
227 if (this._adicional !== null) {
228 this._adicional.classList.add("scroll")
229 }
230 }
231 this._scrolling = false
232 }
233
234}
235
236customElements.define("md-top-app-bar", MdTopAppBar)

H. lib / js / custom / MdNavigationDrawer.js

1import { abreElementoHtml } from "../abreElementoHtml.js"
2import { cierraElementoHtmo } from "../cierraElementoHtmo.js"
3import { querySelector } from "../querySelector.js"
4
5export class MdNavigationDrawer extends HTMLElement {
6
7 /**
8 * @returns {string}
9 */
10 getHipervinculos() { throw new Error("abstract") }
11
12 getContent() {
13 return /* HTML */`
14
15 <link rel="stylesheet" href="/lib/css/material-symbols-outlined.css">
16 <link rel="stylesheet" href="/lib/css/md-ripple.css">
17 <link rel="stylesheet" href="/material-tokens/css/shape.css">
18 <link rel="stylesheet" href="/material-tokens/css/motion.css">
19
20 <style>
21
22 :host {
23 display: block;
24 }
25
26 :host([hidden]) {
27 display: none;
28 }
29
30 nav {
31 display: none;
32 flex-direction: column;
33 position: fixed;
34 z-index: 4;
35 box-sizing: border-box;
36 top: 0;
37 left: 0;
38 bottom: 0;
39 width: var(--anchoNav);
40 max-width: 80vw;
41 overflow: hidden;
42 overscroll-behavior: contain;
43 background-color: var(--md-sys-color-surface-container-low);
44 transform: translateX(-100%);
45 transition-property: display, transform;
46 transition-behavior: allow-discrete;
47 }
48
49 nav.open {
50 display: flex;
51 transform: translateX(0);
52 }
53
54 nav>div {
55 flex-grow: 1;
56 overflow: auto;
57 padding: 0.75rem 1rem;
58 }
59
60 h1 {
61 margin: 0;
62 height: 3.5rem;
63 line-height: 3.5rem;
64 padding: 0 0 0 0.75rem;
65 white-space: nowrap;
66 text-overflow: ellipsis;
67 overflow: hidden;
68 color: var(--md-sys-color-on-surface-variant);
69 font-family: var(--md-sys-typescale-title-small-font);
70 font-weight: var(--md-sys-typescale-title-small-weight);
71 font-size: var(--md-sys-typescale-title-small-size);
72 font-style: var(--md-sys-typescale-title-small-font-style);
73 letter-spacing: var(--md-sys-typescale-title-small-tracking);
74 text-transform: var(--md-sys-typescale-title-small-text-transform);
75 text-decoration: var(--md-sys-typescale-title-small-text-decoration);
76 }
77
78 a::after { /* container inactive */
79 content: "";
80 position: absolute;
81 z-index: -2;
82 top: 0;
83 right: 0;
84 left: 0;
85 bottom: 0;
86 }
87
88 a.active::after { /* container */
89 background-color: var(--md-sys-color-secondary-container);
90 }
91
92 a { /* label, shape inactive */
93 position: relative;
94 display: block;
95 box-sizing: border-box;
96 height: 3.5rem;
97 line-height: 3.5rem;
98 padding: 0 0.75rem;
99 border-radius: 1.75rem;
100 color: var(--md-sys-color-on-surface-variant);
101 font-family: var(--md-sys-typescale-label-large-font);
102 font-weight: var(--md-sys-typescale-label-large-weight);
103 font-size: var(--md-sys-typescale-label-large-size);
104 font-style: var(--md-sys-typescale-label-large-font-style);
105 letter-spacing: var(--md-sys-typescale-label-large-tracking);
106 text-transform: var(--md-sys-typescale-label-large-text-transform);
107 text-decoration: var(--md-sys-typescale-label-large-text-decoration);
108 overflow: hidden;
109 white-space: nowrap;
110 text-overflow: ellipsis;
111 }
112
113 a.active { /* label, shape */
114 font-weight: var(--md-sys-typescale-label-large-weight-prominent);
115 color: var(--md-sys-color-on-secondary-container);
116 }
117
118 a::before { /* state layer */
119 content: "";
120 position: absolute;
121 z-index: -1;
122 top: 0;
123 right: 0;
124 left: 0;
125 bottom: 0;
126 }
127
128 a span { /* inactive icon */
129 position: relative;
130 margin-right: 0.75rem;
131 vertical-align: middle;
132 color: var(--md-sys-color-on-surface-variant);
133 font-size: 1.5rem;
134 width: 1.5rem;
135 height: 1.5rem;
136 }
137
138 a.active span { /* icon */
139 color: var(--md-sys-color-on-secondary-container);
140 }
141
142 #scrim {
143 display: none;
144 position: fixed;
145 z-index: 3;
146 top: 0;
147 left: 0;
148 bottom: 0;
149 right: 0;
150 opacity: 0.4;
151 background-color: var(--md-ref-palette-neutral-variant20);
152 transform: translateX(-100%);
153 transition-property: display, transform;
154 transition-behavior: allow-discrete;
155 }
156
157 #scrim.open {
158 display: block;
159 transform: translateX(0);
160 }
161
162 @starting-style {
163 nav.open{
164 display: flex;
165 transform: translateX(-100%);
166 }
167 #scrim.open {
168 display: block;
169 transform: translateX(-100%);
170 }
171 }
172
173 a:hover { /* inactive label, shape */
174 color: var(--md-sys-color-on-surface);
175 }
176
177 a.active:hover { /* active label, shape */
178 color: var(--md-sys-color-on-secondary-container);
179 }
180
181 a:hover::before { /* inactive state layer */
182 background-color: var(--md-sys-color-on-surface);
183 opacity: var(--md-sys-state-hover-state-layer-opacity);
184 }
185
186 a.active:hover::before { /* state layer */
187 background-color: var(--md-sys-color-on-secondary-container);
188 }
189
190 a:hover span { /* inactive icon */
191 color: var(--md-sys-color-on-surface);
192 }
193
194 a.active:hover span { /* icon */
195 color: var(--md-sys-color-on-secondary-container);
196 }
197
198 a:focus { /* inactive label, shape */
199 outline: none;
200 color: var(--md-sys-color-on-surface);
201 }
202
203 a.active:focus { /* label, shape */
204 color: var(--md-sys-color-on-secondary-container);
205 }
206
207 a:focus::before { /* inactive state layer */
208 background-color: var(--md-sys-color-on-surface);
209 opacity: var(--md-sys-state-focus-state-layer-opacity);
210 }
211
212 a.active:focus::before { /* state layer */
213 background-color: var(--md-sys-color-on-secondary-container);
214 }
215
216 a:focus span { /* inactive icon */
217 color: var(--md-sys-color-on-surface);
218 }
219
220 a.active:focus span { /* icon */
221 color: var(--md-sys-color-on-secondary-container);
222 }
223
224 a:active { /* inactive pressed label, shape */
225 background-position: center;
226 background-image:
227 radial-gradient(circle, var(--md-riple-color) 1%, transparent 1%);
228 background-size: 100%;
229 animation-name: md-ripple;
230 animation-duration: var(--md-sys-motion-duration-500);
231 color: var(--md-sys-color-on-surface);
232 }
233
234 a.active:active { /* active pressed label, shape */
235 color: var(--md-sys-color-on-secondary-container);
236 }
237
238 a:active::before { /* inactive pressed state layer */
239 background-color: var(--md-sys-color-on-surface);
240 opacity: var(--md-sys-state-pressed-state-layer-opacity);
241 }
242
243 a.active:active::before { /* active pressed state layer */
244 background-color: var(--md-sys-color-on-secondary-container);
245 }
246
247 a:active span { /* inactive pressed icon */
248 color: var(--md-sys-color-on-surface);
249 }
250
251 a.active:focus span { /* active pressed icon */
252 color: var(--md-sys-color-on-secondary-container);
253 }
254
255 </style>
256
257 <div id="scrim"class="duration-700 easing-standard"></div>
258 <nav class="large-end duration-700 easing-standard"><div></div></nav>`
259 }
260
261 constructor() {
262 super()
263 const shadow = this.attachShadow({ mode: "open", delegatesFocus: true })
264 shadow.innerHTML = this.getContent()
265 this.cierra = this.cierra.bind(this)
266
267 /** @type {HTMLElement} */
268 this._nav = querySelector(shadow, "nav")
269
270 /** @type {HTMLUListElement} */
271 this._div = querySelector(this._nav, "div")
272
273 /** @type {HTMLUListElement} */
274 this._scrim = querySelector(shadow, "#scrim")
275 this._scrim.addEventListener("click", this.cierra)
276 }
277
278 connectedCallback() {
279 this.classList.add("drawer")
280 this._div.innerHTML = this.getHipervinculos()
281 }
282
283 abre() {
284 abreElementoHtml(this._nav)
285 abreElementoHtml(this._scrim)
286 }
287
288 cierra() {
289 cierraElementoHtmo(this._nav)
290 cierraElementoHtmo(this._scrim)
291 }
292
293}