react-cheminfo 0.41.0 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/bin/check-deploy.mjs +3 -1
  2. package/lib/chrome/ui/AboutButton.d.ts +59 -0
  3. package/lib/chrome/ui/AboutButton.d.ts.map +1 -0
  4. package/lib/chrome/ui/AboutButton.js +29 -0
  5. package/lib/chrome/ui/AboutButton.js.map +1 -0
  6. package/lib/chrome/ui/index.d.ts +2 -0
  7. package/lib/chrome/ui/index.d.ts.map +1 -1
  8. package/lib/chrome/ui/index.js +1 -0
  9. package/lib/chrome/ui/index.js.map +1 -1
  10. package/lib/deploy/core/buildGate.d.ts.map +1 -1
  11. package/lib/deploy/core/buildGate.js +4 -34
  12. package/lib/deploy/core/buildGate.js.map +1 -1
  13. package/lib/deploy/core/checkDeploy.d.ts.map +1 -1
  14. package/lib/deploy/core/checkDeploy.js +2 -0
  15. package/lib/deploy/core/checkDeploy.js.map +1 -1
  16. package/lib/deploy/core/index.d.ts +1 -0
  17. package/lib/deploy/core/index.d.ts.map +1 -1
  18. package/lib/deploy/core/index.js +1 -0
  19. package/lib/deploy/core/index.js.map +1 -1
  20. package/lib/deploy/core/json.d.ts +27 -0
  21. package/lib/deploy/core/json.d.ts.map +1 -0
  22. package/lib/deploy/core/json.js +56 -0
  23. package/lib/deploy/core/json.js.map +1 -0
  24. package/lib/deploy/core/reactCopies.d.ts +32 -0
  25. package/lib/deploy/core/reactCopies.d.ts.map +1 -0
  26. package/lib/deploy/core/reactCopies.js +212 -0
  27. package/lib/deploy/core/reactCopies.js.map +1 -0
  28. package/lib/deploy/core/types.d.ts +1 -1
  29. package/lib/deploy/core/types.d.ts.map +1 -1
  30. package/lib/deploy/core/types.js.map +1 -1
  31. package/lib/ecosystem/core/sites.d.ts.map +1 -1
  32. package/lib/ecosystem/core/sites.js +12 -0
  33. package/lib/ecosystem/core/sites.js.map +1 -1
  34. package/lib/ecosystem/core/types.d.ts +1 -1
  35. package/lib/ecosystem/core/types.d.ts.map +1 -1
  36. package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
  37. package/lib/ecosystem/ui/glyphs.js +4 -0
  38. package/lib/ecosystem/ui/glyphs.js.map +1 -1
  39. package/lib/i18n/core/chromeCatalog.d.ts +1 -1
  40. package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
  41. package/lib/locales/de.json +2 -0
  42. package/lib/locales/en.json +2 -0
  43. package/lib/locales/es.json +2 -0
  44. package/lib/locales/fr.json +2 -0
  45. package/lib/locales/it.json +2 -0
  46. package/lib/periodic/ui/ElementCell.d.ts +22 -2
  47. package/lib/periodic/ui/ElementCell.d.ts.map +1 -1
  48. package/lib/periodic/ui/ElementCell.js +20 -145
  49. package/lib/periodic/ui/ElementCell.js.map +1 -1
  50. package/lib/periodic/ui/PeriodicTable.d.ts +7 -0
  51. package/lib/periodic/ui/PeriodicTable.d.ts.map +1 -1
  52. package/lib/periodic/ui/PeriodicTable.js +21 -3
  53. package/lib/periodic/ui/PeriodicTable.js.map +1 -1
  54. package/lib/periodic/ui/cellStyles.d.ts +79 -0
  55. package/lib/periodic/ui/cellStyles.d.ts.map +1 -0
  56. package/lib/periodic/ui/cellStyles.js +90 -0
  57. package/lib/periodic/ui/cellStyles.js.map +1 -0
  58. package/lib/periodic/ui/cellType.d.ts +106 -0
  59. package/lib/periodic/ui/cellType.d.ts.map +1 -0
  60. package/lib/periodic/ui/cellType.js +181 -0
  61. package/lib/periodic/ui/cellType.js.map +1 -0
  62. package/lib/periodic/ui/glyphWidths.d.ts +11 -0
  63. package/lib/periodic/ui/glyphWidths.d.ts.map +1 -0
  64. package/lib/periodic/ui/glyphWidths.js +69 -0
  65. package/lib/periodic/ui/glyphWidths.js.map +1 -0
  66. package/package.json +9 -1
  67. package/src/chrome/ui/AboutButton.tsx +90 -0
  68. package/src/chrome/ui/index.ts +2 -0
  69. package/src/deploy/core/buildGate.ts +4 -35
  70. package/src/deploy/core/checkDeploy.ts +7 -0
  71. package/src/deploy/core/index.ts +1 -0
  72. package/src/deploy/core/json.ts +53 -0
  73. package/src/deploy/core/reactCopies.ts +243 -0
  74. package/src/deploy/core/types.ts +5 -1
  75. package/src/ecosystem/core/sites.ts +13 -0
  76. package/src/ecosystem/core/types.ts +2 -1
  77. package/src/ecosystem/ui/glyphs.tsx +16 -0
  78. package/src/locales/de.json +2 -0
  79. package/src/locales/en.json +2 -0
  80. package/src/locales/es.json +2 -0
  81. package/src/locales/fr.json +2 -0
  82. package/src/locales/it.json +2 -0
  83. package/src/periodic/ui/ElementCell.tsx +69 -160
  84. package/src/periodic/ui/PeriodicTable.tsx +37 -2
  85. package/src/periodic/ui/cellStyles.ts +100 -0
  86. package/src/periodic/ui/cellType.ts +223 -0
  87. package/src/periodic/ui/glyphWidths.ts +68 -0
  88. package/styles/chrome.css +10 -2
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "language.label": "Langue",
3
3
  "chrome.pages": "Pages",
4
+ "chrome.about": "À propos",
5
+ "chrome.aboutTitle": "Ce qu'est ce site, sur quoi il est bâti et comment le citer",
4
6
  "account.signIn": "Se connecter",
5
7
  "account.signOut": "Se déconnecter",
6
8
  "account.signedInAs": "Connecté comme {name}",
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "language.label": "Lingua",
3
3
  "chrome.pages": "Pagine",
4
+ "chrome.about": "Informazioni",
5
+ "chrome.aboutTitle": "Cosa è questo sito, su cosa si basa e come citarlo",
4
6
  "account.signIn": "Accedi",
5
7
  "account.signOut": "Esci",
6
8
  "account.signedInAs": "Accesso effettuato come {name}",
@@ -1,6 +1,7 @@
1
1
  /**
2
- * One cell of the periodic table: the atomic number, the symbol, and whatever
3
- * the tool asked to be written under it.
2
+ * One cell of the periodic table: the atomic number, the symbol, whatever the
3
+ * tool asked to be written under it, and — when the tool asks for them — the
4
+ * electrons of each shell down the right-hand edge.
4
5
  *
5
6
  * A cell is a real `<button>`, so Tab reaches it, Enter and Space activate it,
6
7
  * and a screen reader reads the element's name rather than its symbol.
@@ -10,13 +11,33 @@
10
11
  * screen — the table a class reads is the same drawing, larger.
11
12
  */
12
13
 
13
- import type { CSSProperties, ReactElement } from 'react';
14
+ import type { ReactElement } from 'react';
14
15
  import { useState } from 'react';
15
16
 
16
17
  import type { Swatch } from '../../color/core/interpolate.ts';
17
18
  import { useChromeT } from '../../i18n/ui/useT.ts';
18
19
 
19
- import { UNIT, ofWidth } from './unit.ts';
20
+ import {
21
+ SHELLS_AIR,
22
+ SHELLS_COLUMN,
23
+ cellStyle,
24
+ detailStyle,
25
+ numberStyle,
26
+ shellsStyle,
27
+ symbolStyle,
28
+ } from './cellStyles.ts';
29
+ import type { DetailType } from './cellType.ts';
30
+ import {
31
+ SHELLS_RESERVED,
32
+ SYMBOL_CAP,
33
+ condensedScale,
34
+ detailType,
35
+ scaleX,
36
+ shellFontSize,
37
+ symbolFontSize,
38
+ textWidthInEm,
39
+ } from './cellType.ts';
40
+ import { ofWidth } from './unit.ts';
20
41
 
21
42
  /** What {@link ElementCell} needs to draw one element. */
22
43
  export interface ElementCellProps {
@@ -39,6 +60,24 @@ export interface ElementCellProps {
39
60
  * @default '' — nothing is written
40
61
  */
41
62
  detail?: string;
63
+ /**
64
+ * The size that line is written at, from {@link detailType}, so a whole
65
+ * table writes it at one size rather than each cell at its own.
66
+ * @default the largest size this cell's own line fits at
67
+ */
68
+ detailType?: DetailType;
69
+ /**
70
+ * Electrons in each shell, innermost first, written down the right-hand
71
+ * edge.
72
+ * @default undefined — nothing is written there
73
+ */
74
+ shells?: readonly number[];
75
+ /**
76
+ * The size they are written at, so the deepest stack in the table fits its
77
+ * cell and every other is written to match.
78
+ * @default the size this cell's own stack fits at
79
+ */
80
+ shellSize?: string;
42
81
  /**
43
82
  * Whether the cell is the one the tools are pointed at.
44
83
  * @default false
@@ -78,6 +117,7 @@ export function ElementCell(props: ElementCellProps): ReactElement {
78
117
  row,
79
118
  onSelect,
80
119
  detail = '',
120
+ shells,
81
121
  isSelected = false,
82
122
  isIncluded = true,
83
123
  onHover,
@@ -85,6 +125,11 @@ export function ElementCell(props: ElementCellProps): ReactElement {
85
125
  const t = useChromeT();
86
126
  const [isHovered, setIsHovered] = useState(false);
87
127
 
128
+ const detailWidth = textWidthInEm(detail);
129
+ const reserved = shells === undefined ? 0 : SHELLS_RESERVED;
130
+ const type = props.detailType ?? detailType([detailWidth], reserved);
131
+ const shellSize = props.shellSize ?? shellFontSize(shells?.length ?? 0);
132
+
88
133
  return (
89
134
  <button
90
135
  type="button"
@@ -113,6 +158,11 @@ export function ElementCell(props: ElementCellProps): ReactElement {
113
158
  ...cellStyle,
114
159
  gridColumn: column,
115
160
  gridRow: row,
161
+ // The electrons stand in a column of their own, so the symbol keeps
162
+ // the left of the cell rather than being pushed off its centre.
163
+ gridTemplateColumns:
164
+ shells === undefined ? '1fr' : `1fr ${SHELLS_COLUMN}`,
165
+ columnGap: shells === undefined ? undefined : SHELLS_AIR,
116
166
  background: swatch.background,
117
167
  color: swatch.foreground,
118
168
  opacity: isIncluded ? 1 : 0.28,
@@ -132,170 +182,29 @@ export function ElementCell(props: ElementCellProps): ReactElement {
132
182
  <span
133
183
  style={{
134
184
  ...symbolStyle,
135
- fontSize: symbolFontSize(symbol),
185
+ fontSize: symbolFontSize(symbol, SYMBOL_CAP, reserved),
136
186
  }}
137
187
  >
138
188
  {symbol}
139
189
  </span>
140
190
  {detail === '' ? null : (
141
- <span style={{ ...detailStyle, fontSize: detailFontSize(detail) }}>
191
+ <span
192
+ style={{
193
+ ...detailStyle,
194
+ fontSize: type.size,
195
+ // Condensed rather than written smaller, so every cell of the
196
+ // table says its name and its value in one type.
197
+ transform: scaleX(condensedScale(detailWidth, type, reserved)),
198
+ }}
199
+ >
142
200
  {detail}
143
201
  </span>
144
202
  )}
203
+ {shells === undefined ? null : (
204
+ <span style={{ ...shellsStyle, fontSize: shellSize }}>
205
+ {shells.join('\n')}
206
+ </span>
207
+ )}
145
208
  </button>
146
209
  );
147
210
  }
148
-
149
- /**
150
- * The size the symbol is written at.
151
- *
152
- * Every symbol is written at the same size, except the widest of them — the
153
- * ones whose second letter is an `m` — which are written a tenth smaller
154
- * rather than run into the edges of their cell.
155
- * @param symbol - The chemical symbol.
156
- * @returns A CSS length.
157
- */
158
- function symbolFontSize(symbol: string): string {
159
- return fitted(symbolWidthInEm(symbol), SYMBOL_CAP, '0.5rem');
160
- }
161
-
162
- /**
163
- * The size the third line takes so the whole of it is written.
164
- *
165
- * A value cut short by an ellipsis is a wrong value — `0.0899` read as `0.0…`
166
- * is worse than not writing it at all — so a long string is written smaller
167
- * rather than trimmed.
168
- * @param detail - What the third line says.
169
- * @returns A CSS length.
170
- */
171
- function detailFontSize(detail: string): string {
172
- return fitted(detailWidthInEm(detail), DETAIL_CAP, '0.25rem');
173
- }
174
-
175
- /**
176
- * The largest size a string of the given width fits its cell at.
177
- *
178
- * The room a cell leaves is a share of the table's width less its border and
179
- * its padding, which are the same few pixels at every size — hence the two
180
- * units in one expression.
181
- * @param widthInEm - How wide the string is, in ems of its own size.
182
- * @param cap - The size it is written at when it has room to spare.
183
- * @param floor - The size under which it is not shrunk any further.
184
- * @returns A CSS length.
185
- */
186
- function fitted(widthInEm: number, cap: string, floor: string): string {
187
- const room = `calc((${UNIT} * ${String(CELL_SHARE)} - ${CELL_CHROME}) / ${widthInEm.toFixed(2)})`;
188
- return `max(${floor}, min(${cap}, ${room}))`;
189
- }
190
-
191
- /**
192
- * How wide a symbol is, in ems of its own size.
193
- * @param symbol - The chemical symbol.
194
- * @returns Its width, never zero.
195
- */
196
- function symbolWidthInEm(symbol: string): number {
197
- let width = 0;
198
- for (const character of symbol) {
199
- if (character === 'm' || character === 'w') {
200
- width += 0.95;
201
- } else if (character >= 'a' && character <= 'z') {
202
- width += 0.63;
203
- } else {
204
- width += 0.85;
205
- }
206
- }
207
- return Math.max(width, 0.85);
208
- }
209
-
210
- /**
211
- * How wide the third line is, in ems of its own size.
212
- *
213
- * It is written with tabular figures, where every glyph — the decimal point
214
- * and the minus of an exponent included — advances the same width, so counting
215
- * the characters is the measurement.
216
- * @param detail - What the third line says.
217
- * @returns Its width, never zero.
218
- */
219
- function detailWidthInEm(detail: string): number {
220
- return Math.max(detail.length, 1) * TABULAR_EM;
221
- }
222
-
223
- /** What one tabular glyph advances, as a share of its own size. */
224
- const TABULAR_EM = 0.6;
225
-
226
- /** The size the symbol is written at wherever it fits, as a share of the table. */
227
- const SYMBOL_CAP = ofWidth(2.4);
228
-
229
- /**
230
- * The tallest the third line is ever written, as a share of the table.
231
- *
232
- * Close to two thirds of the symbol: the symbol is how a cell is found, but
233
- * the value is what the table is being read for, and a value half the size of
234
- * the symbol is the one thing on the cell a class cannot make out.
235
- */
236
- const DETAIL_CAP = ofWidth(1.6);
237
-
238
- /**
239
- * The band at each end of a cell: the atomic number at the top, whatever the
240
- * tool writes at the bottom.
241
- *
242
- * They are one length, not two, and that is what makes the symbol between them
243
- * sit at the centre of the cell rather than near it — a bottom band wider than
244
- * the top one by a third lifted every symbol in the table two pixels.
245
- */
246
- const EDGE_BAND = ofWidth(1.8);
247
-
248
- /** How wide one column is, in per cent of the table's width. */
249
- const CELL_SHARE = 4.85;
250
-
251
- /** What the border and the padding of a cell take off that width. */
252
- const CELL_CHROME = '4px';
253
-
254
- /**
255
- * A cell is three bands of its own, and every cell has all three.
256
- *
257
- * The atomic number is in the top band, against its left edge; the symbol is
258
- * centred in the middle one; whatever the tool writes is centred in the
259
- * bottom one. The bands keep their height whether or not anything is written
260
- * in the last of them, so a number sits at one height across the whole table
261
- * and a symbol at one other — before this, both rode on a stack the cell
262
- * centred and moved whenever a neighbour's value was written smaller.
263
- */
264
- const cellStyle = {
265
- border: '1px solid rgb(255 255 255 / 0.55)',
266
- borderRadius: 3,
267
- cursor: 'pointer',
268
- display: 'grid',
269
- font: 'inherit',
270
- gridTemplateRows: `${EDGE_BAND} 1fr ${EDGE_BAND}`,
271
- justifyItems: 'center',
272
- minWidth: 0,
273
- overflow: 'hidden',
274
- padding: '1px',
275
- transition: 'opacity 120ms ease, filter 120ms ease',
276
- } as const satisfies CSSProperties;
277
-
278
- const numberStyle = {
279
- alignSelf: 'start',
280
- fontSize: `max(0.33rem, ${ofWidth(1.1)})`,
281
- fontVariantNumeric: 'tabular-nums',
282
- justifySelf: 'start',
283
- lineHeight: 1,
284
- opacity: 0.8,
285
- } as const satisfies CSSProperties;
286
-
287
- const symbolStyle = {
288
- alignSelf: 'center',
289
- fontWeight: 700,
290
- letterSpacing: '-0.02em',
291
- lineHeight: 1.02,
292
- } as const satisfies CSSProperties;
293
-
294
- const detailStyle = {
295
- alignSelf: 'center',
296
- fontVariantNumeric: 'tabular-nums',
297
- lineHeight: 1,
298
- maxWidth: '100%',
299
- overflow: 'hidden',
300
- whiteSpace: 'nowrap',
301
- } as const satisfies CSSProperties;
@@ -37,6 +37,13 @@ import {
37
37
  HeaderStrips,
38
38
  InnerTransitionMarkers,
39
39
  } from './PeriodicTableChrome.tsx';
40
+ import {
41
+ ROW_SHARE,
42
+ SHELLS_RESERVED,
43
+ detailType,
44
+ shellFontSize,
45
+ textWidthInEm,
46
+ } from './cellType.ts';
40
47
  import { UNIT_PROPERTY, ofWidth, unitValue } from './unit.ts';
41
48
 
42
49
  /** What {@link PeriodicTable} needs. */
@@ -68,6 +75,13 @@ export interface PeriodicTableProps {
68
75
  * @default the English name
69
76
  */
70
77
  nameOf?: (element: PeriodicElement) => string;
78
+ /**
79
+ * Electrons in each shell of an element, innermost first, written down the
80
+ * right-hand edge of its cell. The energy levels, which a table is also read
81
+ * for: none of ours knows them, so the tool that does says so here.
82
+ * @default undefined — no cell writes any
83
+ */
84
+ shellsOf?: (element: PeriodicElement) => readonly number[] | undefined;
71
85
  /**
72
86
  * Whether an element is inside the set the tool is showing. Everything
73
87
  * outside it is dimmed rather than removed, so the table keeps its shape.
@@ -137,6 +151,7 @@ export function PeriodicTable(props: PeriodicTableProps): ReactElement {
137
151
  swatchOf = defaultSwatchOf,
138
152
  detailOf,
139
153
  nameOf = defaultNameOf,
154
+ shellsOf,
140
155
  isIncluded,
141
156
  inset,
142
157
  headers = false,
@@ -184,6 +199,23 @@ export function PeriodicTable(props: PeriodicTableProps): ReactElement {
184
199
  onSelect(next.symbol);
185
200
  }
186
201
 
202
+ // One size for the whole table rather than one per cell: a column of values
203
+ // written at eleven sizes is read as eleven things, and a name shrunk to a
204
+ // third of its neighbour's is the one a class cannot make out. The size is
205
+ // what the widest of them fits at, and anything wider than that is condensed
206
+ // by the cell rather than written smaller still.
207
+ const placed = placedElements();
208
+ const widths: number[] = [];
209
+ let deepest = 0;
210
+ for (const { element } of placed) {
211
+ widths.push(textWidthInEm(detailOf?.(element) ?? ''));
212
+ const stack = shellsOf?.(element);
213
+ if (stack !== undefined && stack.length > deepest) deepest = stack.length;
214
+ }
215
+ const reserved = deepest === 0 ? 0 : SHELLS_RESERVED;
216
+ const type = detailType(widths, reserved);
217
+ const shellSize = shellFontSize(deepest);
218
+
187
219
  return (
188
220
  <div ref={rootRef} className={className} style={rootStyle}>
189
221
  <div
@@ -199,13 +231,16 @@ export function PeriodicTable(props: PeriodicTableProps): ReactElement {
199
231
  <div style={insetStyle(offset)}>{inset}</div>
200
232
  )}
201
233
  {markers ? <InnerTransitionMarkers offset={offset} /> : null}
202
- {placedElements().map(({ element, cell }) => (
234
+ {placed.map(({ element, cell }) => (
203
235
  <ElementCell
204
236
  key={element.symbol}
205
237
  atomicNumber={element.atomicNumber}
206
238
  symbol={element.symbol}
207
239
  name={nameOf(element)}
208
240
  detail={detailOf?.(element)}
241
+ detailType={type}
242
+ shells={shellsOf?.(element)}
243
+ shellSize={shellSize}
209
244
  swatch={swatchOf(element)}
210
245
  isSelected={element.symbol === selected}
211
246
  isIncluded={isIncluded?.(element)}
@@ -270,7 +305,7 @@ const MIN_WIDTH = 280;
270
305
  * table: a cell keeps the band it writes a value in whether or not that table
271
306
  * writes one.
272
307
  */
273
- const ROW_HEIGHT = ofWidth(6.8);
308
+ const ROW_HEIGHT = ofWidth(ROW_SHARE);
274
309
 
275
310
  /** The band the two inner-transition series were lifted out across. */
276
311
  const SERIES_GAP = `max(6px, ${ofWidth(1.1)})`;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * The three bands a cell is laid out as, and the column the electrons stand in.
3
+ *
4
+ * Kept beside the cell rather than inside it, because what they say — where the
5
+ * atomic number sits, how tall a band is — is read far more often than the
6
+ * markup that carries them.
7
+ */
8
+
9
+ import type { CSSProperties } from 'react';
10
+
11
+ import { SHELLS_GAP, SHELLS_SHARE, SHELL_LEADING } from './cellType.ts';
12
+ import { ofWidth } from './unit.ts';
13
+
14
+ /** The column the electron counts stand in, as a CSS length. */
15
+ export const SHELLS_COLUMN = ofWidth(SHELLS_SHARE);
16
+
17
+ /** The air between the symbol and that column, as a CSS length. */
18
+ export const SHELLS_AIR = ofWidth(SHELLS_GAP);
19
+
20
+ /**
21
+ * The band at each end of a cell: the atomic number at the top, whatever the
22
+ * tool writes at the bottom.
23
+ *
24
+ * They are one length, not two, and that is what makes the symbol between them
25
+ * sit at the centre of the cell rather than near it — a bottom band wider than
26
+ * the top one by a third lifted every symbol in the table two pixels.
27
+ */
28
+ const EDGE_BAND = ofWidth(1.8);
29
+
30
+ /**
31
+ * A cell is three bands of its own, and every cell has all three.
32
+ *
33
+ * The atomic number is in the top band, against its left edge; the symbol is
34
+ * centred in the middle one; whatever the tool writes is centred in the
35
+ * bottom one. The bands keep their height whether or not anything is written
36
+ * in the last of them, so a number sits at one height across the whole table
37
+ * and a symbol at one other — before this, both rode on a stack the cell
38
+ * centred and moved whenever a neighbour's value was written smaller.
39
+ */
40
+ export const cellStyle = {
41
+ border: '1px solid rgb(255 255 255 / 0.55)',
42
+ borderRadius: 3,
43
+ cursor: 'pointer',
44
+ display: 'grid',
45
+ font: 'inherit',
46
+ gridTemplateRows: `${EDGE_BAND} 1fr ${EDGE_BAND}`,
47
+ justifyItems: 'center',
48
+ minWidth: 0,
49
+ overflow: 'hidden',
50
+ padding: '1px',
51
+ transition: 'opacity 120ms ease, filter 120ms ease',
52
+ } as const satisfies CSSProperties;
53
+
54
+ export const numberStyle = {
55
+ alignSelf: 'start',
56
+ gridColumn: 1,
57
+ gridRow: 1,
58
+ fontSize: `max(0.33rem, ${ofWidth(1.1)})`,
59
+ fontVariantNumeric: 'tabular-nums',
60
+ justifySelf: 'start',
61
+ lineHeight: 1,
62
+ opacity: 0.8,
63
+ } as const satisfies CSSProperties;
64
+
65
+ export const symbolStyle = {
66
+ alignSelf: 'center',
67
+ gridColumn: 1,
68
+ gridRow: 2,
69
+ fontWeight: 700,
70
+ letterSpacing: '-0.02em',
71
+ lineHeight: 1.02,
72
+ } as const satisfies CSSProperties;
73
+
74
+ export const detailStyle = {
75
+ alignSelf: 'center',
76
+ gridColumn: 1,
77
+ gridRow: 3,
78
+ fontVariantNumeric: 'tabular-nums',
79
+ lineHeight: 1,
80
+ overflow: 'hidden',
81
+ whiteSpace: 'nowrap',
82
+ } as const satisfies CSSProperties;
83
+
84
+ /**
85
+ * The electrons of each shell, innermost at the top, down the right-hand edge
86
+ * of the whole cell: the stack is as tall as the three bands together, which
87
+ * is what lets seven of them be read.
88
+ */
89
+ export const shellsStyle = {
90
+ alignSelf: 'center',
91
+ gridColumn: 2,
92
+ gridRow: '1 / span 3',
93
+ fontVariantNumeric: 'tabular-nums',
94
+ justifySelf: 'end',
95
+ lineHeight: SHELL_LEADING,
96
+ opacity: 0.85,
97
+ paddingInlineEnd: `max(0.5px, ${ofWidth(0.15)})`,
98
+ textAlign: 'right',
99
+ whiteSpace: 'pre-line',
100
+ } as const satisfies CSSProperties;