@simai-studio/view 0.0.0-stage → 1.0.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 (127) hide show
  1. package/LICENSE.md +183 -0
  2. package/README.md +75 -2
  3. package/dist/package-receipt.json +971 -0
  4. package/dist/set.json +141 -0
  5. package/dist/smart/rule.js +278 -0
  6. package/dist/smart/rule.js.gz +0 -0
  7. package/dist/smart/view/aliases.json +129 -0
  8. package/dist/smart/view/aliases.json.gz +0 -0
  9. package/dist/smart/view/component.json +1620 -0
  10. package/dist/smart/view/component.json.gz +0 -0
  11. package/dist/smart/view/css/view.css +208 -0
  12. package/dist/smart/view/css/view.css.gz +0 -0
  13. package/dist/smart/view/css/view.min.css +2 -0
  14. package/dist/smart/view/css/view.min.css.gz +0 -0
  15. package/dist/smart/view/examples.json +668 -0
  16. package/dist/smart/view/examples.json.gz +0 -0
  17. package/dist/smart/view/js/view.js +3938 -0
  18. package/dist/smart/view/js/view.js.gz +0 -0
  19. package/dist/smart/view/js/view.min.js +1 -0
  20. package/dist/smart/view/js/view.min.js.gz +0 -0
  21. package/dist/smart/view/lang/en.json +61 -0
  22. package/dist/smart/view/lang/en.json.gz +0 -0
  23. package/dist/smart/view/lang/ru.json +61 -0
  24. package/dist/smart/view/lang/ru.json.gz +0 -0
  25. package/dist/smart/view/probe.json +165 -0
  26. package/dist/smart/view/probe.json.gz +0 -0
  27. package/dist/smart/view/smart.manifest.json +1570 -0
  28. package/dist/smart/view/smart.manifest.json.gz +0 -0
  29. package/dist/smart/view/static/default/embedded.html +96 -0
  30. package/dist/smart/view/static/default/embedded.html.gz +0 -0
  31. package/dist/smart/view/static/default/open-record.html +247 -0
  32. package/dist/smart/view/static/default/open-record.html.gz +0 -0
  33. package/dist/smart/view/static/default/reference.html +247 -0
  34. package/dist/smart/view/static/default/reference.html.gz +0 -0
  35. package/dist/smart/view/static/sheets.json +282 -0
  36. package/dist/smart/view/static/sheets.json.gz +0 -0
  37. package/dist/smart/view-properties/aliases.json +174 -0
  38. package/dist/smart/view-properties/aliases.json.gz +0 -0
  39. package/dist/smart/view-properties/component.json +1898 -0
  40. package/dist/smart/view-properties/component.json.gz +0 -0
  41. package/dist/smart/view-properties/css/view-properties.css +1153 -0
  42. package/dist/smart/view-properties/css/view-properties.css.gz +0 -0
  43. package/dist/smart/view-properties/css/view-properties.min.css +2 -0
  44. package/dist/smart/view-properties/css/view-properties.min.css.gz +0 -0
  45. package/dist/smart/view-properties/examples.json +841 -0
  46. package/dist/smart/view-properties/examples.json.gz +0 -0
  47. package/dist/smart/view-properties/js/view-properties.js +10416 -0
  48. package/dist/smart/view-properties/js/view-properties.js.gz +0 -0
  49. package/dist/smart/view-properties/js/view-properties.min.js +1421 -0
  50. package/dist/smart/view-properties/js/view-properties.min.js.gz +0 -0
  51. package/dist/smart/view-properties/lang/en.json +96 -0
  52. package/dist/smart/view-properties/lang/en.json.gz +0 -0
  53. package/dist/smart/view-properties/lang/ru.json +96 -0
  54. package/dist/smart/view-properties/lang/ru.json.gz +0 -0
  55. package/dist/smart/view-properties/probe.json +188 -0
  56. package/dist/smart/view-properties/probe.json.gz +0 -0
  57. package/dist/smart/view-properties/skeleton.html +13 -0
  58. package/dist/smart/view-properties/skeleton.html.gz +0 -0
  59. package/dist/smart/view-properties/smart.manifest.json +1590 -0
  60. package/dist/smart/view-properties/smart.manifest.json.gz +0 -0
  61. package/dist/smart/view-properties/static/default/full.html +2 -0
  62. package/dist/smart/view-properties/static/default/full.html.gz +0 -0
  63. package/dist/smart/view-properties/static/default/panel-closed.html +4 -0
  64. package/dist/smart/view-properties/static/default/panel-closed.html.gz +0 -0
  65. package/dist/smart/view-properties/static/default/panel.html +4 -0
  66. package/dist/smart/view-properties/static/default/panel.html.gz +0 -0
  67. package/dist/smart/view-properties/static/default/reference.html +2 -0
  68. package/dist/smart/view-properties/static/default/reference.html.gz +0 -0
  69. package/dist/smart/view-properties/static/sheets.json +426 -0
  70. package/dist/smart/view-properties/static/sheets.json.gz +0 -0
  71. package/dist/smart/view-properties/template/default.css +989 -0
  72. package/dist/smart/view-properties/template/default.css.gz +0 -0
  73. package/dist/smart/view-properties/template/default.js +1660 -0
  74. package/dist/smart/view-properties/template/default.js.gz +0 -0
  75. package/dist/smart/view-table/aliases.json +448 -0
  76. package/dist/smart/view-table/aliases.json.gz +0 -0
  77. package/dist/smart/view-table/component.json +2738 -0
  78. package/dist/smart/view-table/component.json.gz +0 -0
  79. package/dist/smart/view-table/css/view-table.css +3658 -0
  80. package/dist/smart/view-table/css/view-table.css.gz +0 -0
  81. package/dist/smart/view-table/css/view-table.min.css +2 -0
  82. package/dist/smart/view-table/css/view-table.min.css.gz +0 -0
  83. package/dist/smart/view-table/examples.json +499 -0
  84. package/dist/smart/view-table/examples.json.gz +0 -0
  85. package/dist/smart/view-table/js/view-table.js +13879 -0
  86. package/dist/smart/view-table/js/view-table.js.gz +0 -0
  87. package/dist/smart/view-table/js/view-table.min.js +1440 -0
  88. package/dist/smart/view-table/js/view-table.min.js.gz +0 -0
  89. package/dist/smart/view-table/lang/en.json +132 -0
  90. package/dist/smart/view-table/lang/en.json.gz +0 -0
  91. package/dist/smart/view-table/lang/ru.json +132 -0
  92. package/dist/smart/view-table/lang/ru.json.gz +0 -0
  93. package/dist/smart/view-table/probe.json +275 -0
  94. package/dist/smart/view-table/probe.json.gz +0 -0
  95. package/dist/smart/view-table/smart.manifest.json +2524 -0
  96. package/dist/smart/view-table/smart.manifest.json.gz +0 -0
  97. package/dist/smart/view-table/static/default/current.html +141 -0
  98. package/dist/smart/view-table/static/default/current.html.gz +0 -0
  99. package/dist/smart/view-table/static/default/current.skeleton.html +141 -0
  100. package/dist/smart/view-table/static/default/current.skeleton.html.gz +0 -0
  101. package/dist/smart/view-table/static/default/empty.html +149 -0
  102. package/dist/smart/view-table/static/default/empty.html.gz +0 -0
  103. package/dist/smart/view-table/static/default/loading.html +180 -0
  104. package/dist/smart/view-table/static/default/loading.html.gz +0 -0
  105. package/dist/smart/view-table/static/default/many.html +180 -0
  106. package/dist/smart/view-table/static/default/many.html.gz +0 -0
  107. package/dist/smart/view-table/static/default/reference.html +136 -0
  108. package/dist/smart/view-table/static/default/reference.html.gz +0 -0
  109. package/dist/smart/view-table/static/default/reference.skeleton.html +136 -0
  110. package/dist/smart/view-table/static/default/reference.skeleton.html.gz +0 -0
  111. package/dist/smart/view-table/static/default/typed-cells.html +229 -0
  112. package/dist/smart/view-table/static/default/typed-cells.html.gz +0 -0
  113. package/dist/smart/view-table/static/default/typed-cells.skeleton.html +229 -0
  114. package/dist/smart/view-table/static/default/typed-cells.skeleton.html.gz +0 -0
  115. package/dist/smart/view-table/static/sheets.json +480 -0
  116. package/dist/smart/view-table/static/sheets.json.gz +0 -0
  117. package/dist/smart/view-table/studio.fixture.json +17 -0
  118. package/dist/smart/view-table/studio.fixture.json.gz +0 -0
  119. package/dist/smart/view-table/studio.scenario.json +30 -0
  120. package/dist/smart/view-table/studio.scenario.json.gz +0 -0
  121. package/dist/smart/view-table/template/default.css +1301 -0
  122. package/dist/smart/view-table/template/default.css.gz +0 -0
  123. package/dist/smart/view-table/template/default.js +1215 -0
  124. package/dist/smart/view-table/template/default.js.gz +0 -0
  125. package/dist/smart/view-table/template/link-cell-contract.js +48 -0
  126. package/dist/smart/view-table/template/link-cell-contract.js.gz +0 -0
  127. package/package.json +29 -4
@@ -0,0 +1,1153 @@
1
+ @charset "UTF-8";
2
+ @layer sf.reset, sf.tokens, sf.base, sf.utilities, sf.components, sf.overrides, sf.states;
3
+ @layer sf.components {
4
+ /* The composite renders into its own light DOM, so these rules reach the field
5
+ controls and the Framework's component stylesheets reach them too. The first
6
+ version used a shadow root and every Smart child inside it came out with its
7
+ markup and none of its CSS -- sf-view avoids that by slotting its
8
+ children and says so beside the slot. */
9
+ sf-view-properties {
10
+ display: block;
11
+ }
12
+ sf-property-view {
13
+ display: block;
14
+ }
15
+ /* The record holds its box (first paint, standard 1.3.9 §5.4): inline or as
16
+ the page, it fills the region the page gives it, and the record -- its
17
+ arrangement, how many groups and fields, on a phone the list of its
18
+ sections -- grows inside its own scroll area, so nothing outside it moves
19
+ when the record arrives. In a region of no set height it is as tall as its
20
+ content, as before. The panel is a drawer over the page and holds its own
21
+ box. */
22
+ sf-view-properties:not([panel]) {
23
+ /* Over the base's display: contents, which would leave no box to hold. */
24
+ display: block !important;
25
+ height: 100%;
26
+ block-size: 100%;
27
+ overflow: auto;
28
+ -ms-scroll-chaining: none;
29
+ overscroll-behavior: contain;
30
+ }
31
+ sf-property-view:not([panel]) {
32
+ /* Over the base's display: contents, which would leave no box to hold. */
33
+ display: block !important;
34
+ height: 100%;
35
+ block-size: 100%;
36
+ overflow: auto;
37
+ -ms-scroll-chaining: none;
38
+ overscroll-behavior: contain;
39
+ }
40
+ sf-view-properties[full] {
41
+ /* Over the base's display: contents, which would leave no box to hold. */
42
+ display: block !important;
43
+ height: 100%;
44
+ block-size: 100%;
45
+ overflow: auto;
46
+ -ms-scroll-chaining: none;
47
+ overscroll-behavior: contain;
48
+ }
49
+ sf-property-view[full] {
50
+ /* Over the base's display: contents, which would leave no box to hold. */
51
+ display: block !important;
52
+ height: 100%;
53
+ block-size: 100%;
54
+ overflow: auto;
55
+ -ms-scroll-chaining: none;
56
+ overscroll-behavior: contain;
57
+ }
58
+ /* A stack of windows. Each depth is inset a little further so the one below
59
+ stays partly visible, which is what makes it a stack rather than a
60
+ replacement: a person can see they are two records deep. Only the top window
61
+ is interactive -- a dimmed window that still answered clicks would be a
62
+ window that looks disabled and is not. */
63
+ .sf-property-view-panel[data-stacked=true] {
64
+ opacity: 0.6;
65
+ pointer-events: none;
66
+ }
67
+ .sf-property-view-panel[data-stacked=true]::after {
68
+ content: "";
69
+ position: absolute;
70
+ top: 0;
71
+ right: 0;
72
+ bottom: 0;
73
+ left: 0;
74
+ inset: 0;
75
+ background-color: var(--sf-surface-0);
76
+ opacity: 0.4;
77
+ pointer-events: none;
78
+ }
79
+ /* sf-drawer portals its content out of the element, so an open panel is no
80
+ longer a descendant of sf-view-properties. These rules are scoped to the panel
81
+ itself rather than to the element: written under `sf-view-properties` they
82
+ matched nothing at all -- the shadow-root mistake one layer further out, and
83
+ it would have shipped as a panel with no styles.
84
+
85
+ The panel's own column: the controls above, the body below, and the body
86
+ scrolling rather than the controls scrolling away from the person using them. */
87
+ /* The panel is the tinted surface the cards sit on. White cards on a white panel
88
+ have only their outline to tell them apart, which at a glance is no boundary at
89
+ all -- the state the owner saw. */
90
+ .sf-property-view-panel {
91
+ /* The three rows meet: a gap between them left a band of panel above the
92
+ first card and below the last, which read as an extra margin nobody had
93
+ asked for (owner, 2026-10-08). What spaces the cards is their own. */
94
+ gap: 0;
95
+ /* It grows with its content and the drawer's body scrolls it. Pinned to the
96
+ body's height it made its own scroller, and that scroller's bar sat inside
97
+ the panel's gutter. */
98
+ min-height: 100%;
99
+ min-block-size: 100%;
100
+ /* The drawer paints the surface, through its own `panel-style`, so the whole
101
+ panel is one colour -- head, gutters and body. Painting it here instead
102
+ left a tinted block inside a white one, which is what the owner saw.
103
+ No padding of its own: the inline gutter is the drawer's, and two insets
104
+ stacked is what produced three axes in a consumer's panel. */
105
+ padding: 0;
106
+ }
107
+ /* The panel is laid out the way a page is: a head across the top, a foot
108
+ across the bottom, and what is between them scrolling under both. The two
109
+ bands were sticky inside the panel's own scroller before, which holds a
110
+ band only while its containing block is on screen -- so a record scrolled
111
+ far enough took its own head and foot with it. They are rows of a column
112
+ that does not scroll now, and nothing can carry them off. */
113
+ .sf-property-view-panel {
114
+ /* Grown to fill what the host gives it, not sized at a percentage of it: a
115
+ percentage resolves against the host's height, and a host sized by its
116
+ own content has none to resolve against -- the panel then takes the
117
+ height of the record, and the foot stops wherever the record stops
118
+ instead of standing at the bottom. Growing works either way, because a
119
+ host with height hands it over and a host without one has nothing to
120
+ hand. */
121
+ flex: 1 1 auto;
122
+ min-height: 0;
123
+ min-block-size: 0;
124
+ /* The panel takes the host's block padding back here, as one negative
125
+ margin, rather than letting the head and the bar each reach past it with
126
+ one of their own. Reaching past is overflow, and the drawer's own panel
127
+ is a scroller: the bands fitted into its padding by a pixel, so anything
128
+ that grew by one more made the whole panel scroll -- head, record and
129
+ foot together, which is the foot «travelling» the owner reported. Inside
130
+ the panel's own box there is nothing to scroll. */
131
+ margin-top: calc(-1 * var(--sf-property-view--panel-axis));
132
+ margin-bottom: calc(-1 * var(--sf-property-view--panel-axis));
133
+ margin-block: calc(-1 * var(--sf-property-view--panel-axis));
134
+ /* Not clipped: a popover opened at the panel's edge belongs outside it. */
135
+ overflow: visible;
136
+ }
137
+ /* The one scroller of the panel, and the only thing reserving room for a
138
+ scrollbar. Reserved on both edges: reserved on the end alone, the cards sat
139
+ a scrollbar's width further from the right edge of the panel than from the
140
+ left, which is the difference the owner measured. */
141
+ .sf-property-view-scroll {
142
+ overflow-x: hidden;
143
+ overflow-y: auto;
144
+ overflow: hidden auto;
145
+ scrollbar-gutter: stable both-edges;
146
+ /* A wheel or a trackpad that reaches the end of this column keeps its
147
+ gesture, and the browser hands what is left to whatever is outside --
148
+ which here is the drawer, and the drawer moves the head and the foot with
149
+ it. That is the panel «flying off» after one more scroll at the bottom.
150
+ Contained, the gesture stops where the column stops. */
151
+ -ms-scroll-chaining: none;
152
+ overscroll-behavior: contain;
153
+ /* The step above the first card and below the last one belongs inside the
154
+ scroller, not between it and the head: on the outside it is room nobody
155
+ scrolls through, it holds the cards away from the head for ever, and the
156
+ head has nothing to cover when the record moves. Inside, the record
157
+ starts a step down, travels under the head from there, and the step is
158
+ gone the moment it does (owner, 2026-10-08). */
159
+ padding-top: var(--sf-space-1);
160
+ padding-bottom: var(--sf-space-1);
161
+ padding-block: var(--sf-space-1);
162
+ }
163
+ /* With a rail of tabs what is between the bands is two columns, each with its
164
+ own scrollbar: a long list of sections scrolls without moving the record,
165
+ and a long record scrolls without moving the list. */
166
+ .sf-property-view-panel--railed .sf-property-view-scroll {
167
+ /* Not a utility: the area is a scroller in the plain panel, and a utility
168
+ in the markup cannot beat the component rule that makes it one -- the
169
+ layers put components above utilities. A rule answered by another rule
170
+ stays a rule. */
171
+ overflow: hidden;
172
+ align-items: stretch;
173
+ /* The same step the panel keeps at its edges, so the tabs, the record and
174
+ the panel's sides are one rhythm rather than three. */
175
+ gap: var(--sf-space-1);
176
+ gap: var(--sf-drawer-gutter, var(--sf-space-1));
177
+ scrollbar-gutter: auto;
178
+ }
179
+ /* Each column carries its own step, and the box that holds them both carries
180
+ none: with a rail the outer box does not scroll, so a step on it would be
181
+ the fixed gap again -- the one the owner measured as the panel wasting a
182
+ row of space under the head. The columns do scroll, so the step goes in
183
+ there, where it moves with what it spaces. */
184
+ .sf-property-view-panel--railed .sf-property-view-scroll {
185
+ padding-top: 0;
186
+ padding-bottom: 0;
187
+ padding-block: 0;
188
+ }
189
+ .sf-property-view-panel--railed .sf-property-view-sections,
190
+ .sf-property-view-panel--railed .sf-property-view-frame {
191
+ overflow-x: hidden;
192
+ overflow-y: auto;
193
+ overflow: hidden auto;
194
+ min-height: 0;
195
+ min-block-size: 0;
196
+ padding-top: var(--sf-space-1);
197
+ padding-bottom: var(--sf-space-1);
198
+ padding-block: var(--sf-space-1);
199
+ /* Each column keeps its own gesture, for the reason the plain scroller
200
+ does: past the end of a column the rest of a scroll is the drawer's, and
201
+ the drawer carries the bands. */
202
+ -ms-scroll-chaining: none;
203
+ overscroll-behavior: contain;
204
+ }
205
+ /* The space left of the tabs, between them and the record, and right of the
206
+ record is one step (owner, 2026-10-08): the record's end is the drawer's own
207
+ inline padding, the same step as the gap. It used to be reached by pulling
208
+ the column into that padding with a negative margin, but the scroller that
209
+ holds both columns clips at its own box, so the last pixels of every card
210
+ were cut -- the card «Фон» lost its right border and «Добавить область» ran
211
+ to the edge while the view was arranged (Larena, 2026-10-18). Room for the
212
+ column's scrollbar, so a row's last control does not sit under it, is
213
+ reserved only where the scrollbar takes room. */
214
+ _:-ms-input-placeholder, html:not([dir="rtl"]) .sf-property-view-panel--railed .sf-property-view-frame{
215
+ padding-right: 0;
216
+ }
217
+ @supports not (padding-inline-end: initial){
218
+ html:not([dir="rtl"]) .sf-property-view-panel--railed .sf-property-view-frame{
219
+ padding-right: 0;
220
+ }
221
+ }
222
+ .sf-property-view-panel--railed .sf-property-view-frame:where(:dir(ltr)){
223
+ padding-right: 0;
224
+ }
225
+ _:-ms-input-placeholder, [dir="rtl"] .sf-property-view-panel--railed .sf-property-view-frame{
226
+ padding-left: 0;
227
+ }
228
+ @supports not (padding-inline-end: initial){
229
+ [dir="rtl"] .sf-property-view-panel--railed .sf-property-view-frame{
230
+ padding-left: 0;
231
+ }
232
+ }
233
+ .sf-property-view-panel--railed .sf-property-view-frame:where(:dir(rtl)){
234
+ padding-left: 0;
235
+ }
236
+ .sf-property-view-panel--railed .sf-property-view-frame {
237
+ -webkit-padding-end: 0;
238
+ padding-inline-end: 0;
239
+ scrollbar-gutter: stable;
240
+ }
241
+ .sf-property-view-panel--railed .sf-property-view-sections {
242
+ position: static;
243
+ align-self: stretch;
244
+ }
245
+ /* The body takes the height that is left. It used to scroll itself, which put a
246
+ scrollbar inside the panel's own gutter: the cards ended fourteen pixels
247
+ short of the right edge while the head reached it, so the panel had two
248
+ different margins. The drawer's body scrolls instead, with its bar sticky
249
+ against it and the section list sticking at the top. */
250
+ .sf-property-view-panel > .sf-property-view-layout {
251
+ flex: 1 1 auto;
252
+ min-height: 0;
253
+ min-block-size: 0;
254
+ }
255
+ /* Two columns by the width of the panel, not of the window. A media query asks
256
+ the viewport, and a panel is narrow inside a wide one: a consumer saw one
257
+ column at a 1296px viewport because the panel itself was far narrower. */
258
+ .sf-property-view-layout {
259
+ container-type: inline-size;
260
+ container-name: sf-property-view;
261
+ }
262
+ /* What the body draws is scoped to the element elsewhere in this file, and in a
263
+ portalled panel that scope is gone. These repeat for the panel, so a card is
264
+ a card in both places. */
265
+ .sf-property-view-status[hidden] {
266
+ display: none;
267
+ }
268
+ /* The drawer's body is the scroller, not this. A scroller of its own put a
269
+ scrollbar inside the panel's own gutter, so the cards ended fourteen pixels
270
+ short of the right edge while the head reached it -- two different margins
271
+ on the two sides of the same panel. The section list sticks instead, which
272
+ is what the inner scroller was for. */
273
+ /* The column the section list takes. It is a token because the panel's own
274
+ width is computed from it: the window is the fields' width plus this
275
+ column, so the two numbers must be one number. It is declared on the root
276
+ because the drawer's panel is positioned outside this component's own
277
+ subtree -- read from the element, the calc resolved to nothing and the
278
+ panel silently fell back to a ladder step 336px wide. */
279
+ /* The component's own two tokens, declared on the element and on the panel it
280
+ opens -- never on the page. A composite styles its own subtree, and the
281
+ panel is portalled out of that subtree, so the panel is told them through
282
+ the drawer's `panel-style` rather than through `:root`, which would be this
283
+ component writing on the document. */
284
+ /* The inset the panel's own bands take back, computed from the drawer's own
285
+ tokens. The width of the section column is not declared here: the component
286
+ owns that number, because the panel's width is computed from it outside
287
+ this subtree, and passes it in. */
288
+ sf-view-properties {
289
+ /* What the drawer's body keeps around its content, which the head and the
290
+ bar of actions take back: a band that holds an edge has to reach it. The
291
+ panel reserves no scrollbar room of its own any more -- it scrolls
292
+ nothing, the area between the bands does -- so the gutter is the whole of
293
+ the inset. */
294
+ --sf-property-view--panel-bleed: var(--sf-drawer-gutter, var(--sf-space-1));
295
+ /* Where the content inside the scroller actually starts: the gutter plus the
296
+ room that scroller keeps for its bar on both edges. The two bands reach
297
+ the panel's edges and set their text on this, so the record's name and the
298
+ cards under it stand on one axis. */
299
+ --sf-property-view--panel-axis: calc(
300
+ var(--sf-property-view--panel-bleed) + var(--sf-scroll-bg-width, 0px)
301
+ );
302
+ }
303
+ sf-property-view {
304
+ /* What the drawer's body keeps around its content, which the head and the
305
+ bar of actions take back: a band that holds an edge has to reach it. The
306
+ panel reserves no scrollbar room of its own any more -- it scrolls
307
+ nothing, the area between the bands does -- so the gutter is the whole of
308
+ the inset. */
309
+ --sf-property-view--panel-bleed: var(--sf-drawer-gutter, var(--sf-space-1));
310
+ /* Where the content inside the scroller actually starts: the gutter plus the
311
+ room that scroller keeps for its bar on both edges. The two bands reach
312
+ the panel's edges and set their text on this, so the record's name and the
313
+ cards under it stand on one axis. */
314
+ --sf-property-view--panel-axis: calc(
315
+ var(--sf-property-view--panel-bleed) + var(--sf-scroll-bg-width, 0px)
316
+ );
317
+ }
318
+ .sf-property-view-panel {
319
+ /* What the drawer's body keeps around its content, which the head and the
320
+ bar of actions take back: a band that holds an edge has to reach it. The
321
+ panel reserves no scrollbar room of its own any more -- it scrolls
322
+ nothing, the area between the bands does -- so the gutter is the whole of
323
+ the inset. */
324
+ --sf-property-view--panel-bleed: var(--sf-drawer-gutter, var(--sf-space-1));
325
+ /* Where the content inside the scroller actually starts: the gutter plus the
326
+ room that scroller keeps for its bar on both edges. The two bands reach
327
+ the panel's edges and set their text on this, so the record's name and the
328
+ cards under it stand on one axis. */
329
+ --sf-property-view--panel-axis: calc(
330
+ var(--sf-property-view--panel-bleed) + var(--sf-scroll-bg-width, 0px)
331
+ );
332
+ }
333
+ .sf-property-view-layout {
334
+ display: flex;
335
+ align-items: flex-start;
336
+ gap: var(--sf-space-4);
337
+ }
338
+ /* A rail of tabs and nothing else: the record's name and the band of the
339
+ arranging mode stand over the fields, not over this.
340
+
341
+ It is a surface of its own, a step from the panel's, and it runs to the
342
+ panel's edge: the inline gutter is the drawer's padding, so the rail takes
343
+ it back with a margin and gives it to its own rows. A rail inset from the
344
+ edge read as a floating block of buttons rather than as the side of the
345
+ panel.
346
+
347
+ The tabs are one pixel apart (owner, 2026-10-07): a tinted rail already
348
+ separates them, and a space between each pair made a column of eleven
349
+ sections twice as tall as the names in it. */
350
+ .sf-property-view-sections {
351
+ position: sticky;
352
+ top: 0;
353
+ inset-block-start: 0;
354
+ gap: var(--sf-px);
355
+ /* The rail keeps the panel's own inline step rather than taking it back: at
356
+ the edge the handles and the painted tab were cut by the panel's corner
357
+ (owner, 2026-10-08). */
358
+ padding-left: var(--sf-space-1\/4);
359
+ padding-right: var(--sf-space-1\/4);
360
+ padding-inline: var(--sf-space-1\/4);
361
+ /* Nothing is painted under the tabs (owner, 2026-10-07): a tinted column
362
+ running past the last one drew a block the size of the record rather than
363
+ the size of the list in it. What a tab is, it says by itself. */
364
+ /* Reading, the tabs are a tree, and these are its own tokens: the rail sets
365
+ what it wants rather than reaching into the component's rules. */
366
+ --sf-tree--padding-inline-start: 0;
367
+ --sf-tree--padding-inline-end: 0;
368
+ --sf-tree--padding-top: 0;
369
+ --sf-tree--padding-bottom: 0;
370
+ --sf-tree--gap: var(--sf-px);
371
+ --sf-tree-item--padding-inline: var(--sf-space-1\/3);
372
+ }
373
+ .sf-property-view-section-tabs {
374
+ gap: var(--sf-px);
375
+ }
376
+ /* A tab is a row of its own while the view is arranged: a handle, what it is,
377
+ and its name. It keeps the shape of the tree row it replaces, so the column
378
+ does not change size on the way into the mode. */
379
+ .sf-property-view-section-tab {
380
+ width: 100%;
381
+ inline-size: 100%;
382
+ padding-top: var(--sf-space-1\/3);
383
+ padding-bottom: var(--sf-space-1\/3);
384
+ padding-block: var(--sf-space-1\/3);
385
+ /* The handle stands inside the row rather than on its edge: pressed against
386
+ the painted edge of a selected tab it read as part of the paint. */
387
+ padding-left: var(--sf-space-1\/4);
388
+ padding-right: var(--sf-space-1\/4);
389
+ padding-inline: var(--sf-space-1\/4);
390
+ border: 0;
391
+ background-color: transparent;
392
+ color: inherit;
393
+ font: inherit;
394
+ transition: background-color var(--sf-duration-fast) var(--sf-animation);
395
+ }
396
+ .sf-property-view-section-tab:hover {
397
+ background-color: var(--sf-surface-transparent-hover);
398
+ }
399
+ .sf-property-view-section-tab[aria-current=true] {
400
+ background-color: var(--sf-primary-container);
401
+ color: var(--sf-on-primary-container);
402
+ }
403
+ .sf-property-view-section-tab-label {
404
+ word-wrap: anywhere;
405
+ }
406
+ .sf-property-view-section-grip {
407
+ cursor: -webkit-grab;
408
+ cursor: grab;
409
+ }
410
+ /* The list is a column of its own width and gives the rest to the fields. It
411
+ had `min-inline-size: var(--sf-g0)`, and so did the body -- `--sf-g0` is a
412
+ grid step of 20rem, not zero, so the two floors came to 42rem inside a 40rem
413
+ panel and a sectioned record was clipped at the drawer's edge. Both floors
414
+ are gone; the list keeps a width of its own and a long name wraps in it. */
415
+ .sf-property-view-sections {
416
+ flex: 0 0 var(--sf-property-view--sections-width);
417
+ }
418
+ /* A group is a card, which is what makes a form of twenty fields readable as
419
+ five things rather than twenty. */
420
+ /* A card is white on the panel's tinted surface, so a group reads as a card
421
+ rather than as a band of the same colour. Its padding is one step, not two:
422
+ at space-4 the card was mostly air and a person scrolled past three fields
423
+ where six fit. */
424
+ .sf-property-view-group {
425
+ /* A group decides its own columns. Asking the panel instead kept two columns
426
+ in a 319px card inside a 575px panel -- which is what a section list beside
427
+ the fields leaves -- and every label sat on top of its neighbour's value. */
428
+ container-type: inline-size;
429
+ container-name: sf-property-view-group;
430
+ border: var(--sf-px) solid var(--sf-outline-variant);
431
+ border-radius: var(--sf-radius--surface);
432
+ padding: var(--sf-space-2);
433
+ }
434
+ /* The head carries the group's name and the way into editing it, with a line
435
+ under it: the mockup draws one, and without it the title floated among the
436
+ fields at the same size as their labels. */
437
+ .sf-property-view-group-header {
438
+ padding-bottom: var(--sf-space-1);
439
+ -webkit-padding-after: var(--sf-space-1);
440
+ padding-block-end: var(--sf-space-1);
441
+ border-bottom: var(--sf-px) solid var(--sf-outline-variant);
442
+ -webkit-border-after: var(--sf-px) solid var(--sf-outline-variant);
443
+ border-block-end: var(--sf-px) solid var(--sf-outline-variant);
444
+ }
445
+ /* The loading skeleton's strip of tabs stands where the record's tabs will,
446
+ and the record has tabs only with sections-as tabs and from the wide
447
+ arrangement on (index.js WIDE_MEDIA_QUERY): otherwise it is not drawn. A printed skeleton
448
+ knows no viewport, so the stylesheet decides (standard 1.3.8 §5.4). */
449
+ .sf-property-view-tabs--skeleton:not([data-sections-as=tabs]) {
450
+ display: none;
451
+ }
452
+ @media not all and (min-width: 48rem) {
453
+ .sf-property-view-tabs--skeleton {
454
+ display: none;
455
+ }
456
+ }
457
+ /* The title takes the free space so it starts where the fields under it start.
458
+ Left to `space-between` with a grip before it and an eye after it, a group
459
+ title stood in the middle of its own header. */
460
+ .sf-property-view-group-title {
461
+ margin: 0;
462
+ /* `--sf-title-small` does not exist in this Framework, so this fell back to
463
+ `inherit` and a group title was the same sixteen regular pixels as the field
464
+ labels under it -- the flat look the owner called slapdash. The roles that
465
+ do exist are display, heading, label and text. */
466
+ font-size: var(--sf-heading-6--size);
467
+ font-size: var(--sf-property-view--group-size, var(--sf-heading-6--size));
468
+ line-height: var(--sf-heading-6--height);
469
+ line-height: var(--sf-property-view--group-height, var(--sf-heading-6--height));
470
+ font-weight: 600;
471
+ }
472
+ /* A label is smaller and quieter than its value. They were the same size, so a
473
+ field had no hierarchy at all and a card read as a list of equal lines. */
474
+ .sf-property-view-field-label {
475
+ font-size: var(--sf-label-large--size);
476
+ font-size: var(--sf-property-view--label-size, var(--sf-label-large--size));
477
+ line-height: var(--sf-label-large--height);
478
+ line-height: var(--sf-property-view--label-height, var(--sf-label-large--height));
479
+ }
480
+ .sf-property-view-field-value {
481
+ font-size: var(--sf-text-medium--size);
482
+ font-size: var(--sf-property-view--value-size, var(--sf-text-medium--size));
483
+ line-height: var(--sf-text-medium--height);
484
+ line-height: var(--sf-property-view--value-height, var(--sf-text-medium--height));
485
+ color: var(--sf-on-surface);
486
+ }
487
+ .sf-property-view-group-fields {
488
+ display: grid;
489
+ grid-gap: var(--sf-space-3) var(--sf-space-4);
490
+ gap: var(--sf-space-3) var(--sf-space-4);
491
+ grid-template-columns: 1fr;
492
+ }
493
+ .sf-property-view-group[data-columns="2"] .sf-property-view-group-fields {
494
+ grid-template-columns: repeat(2, minmax(0, 1fr));
495
+ }
496
+ /* After the two-column rule, not before it. A container query adds no
497
+ specificity, and this carries the same selector as the rule above, so while it
498
+ stood earlier in the file the later one won and the query never did anything --
499
+ a two-column group stayed two columns in a 390px panel, wrapping every label
500
+ and every value onto two lines. A comment in this file claimed the columns
501
+ followed the panel's width through this query; they did not, and no measurement
502
+ said so until a consumer asked why their phone looked like that. */
503
+ @container sf-property-view-group (max-width: 29rem) {
504
+ .sf-property-view-group[data-columns="2"] .sf-property-view-group-fields {
505
+ grid-template-columns: 1fr;
506
+ }
507
+ }
508
+ @container sf-property-view (max-width: 35rem) {
509
+ /* The section list stacks above the fields by the panel's own width. The
510
+ viewport rule below does the same thing by the window's width, which is the
511
+ wrong question for a panel: a narrow panel inside a wide window kept a
512
+ 14rem column beside fields that had nothing left to sit in. */
513
+ .sf-property-view-layout {
514
+ display: block;
515
+ }
516
+ .sf-property-view-sections {
517
+ margin-bottom: var(--sf-space-3);
518
+ -webkit-margin-after: var(--sf-space-3);
519
+ margin-block-end: var(--sf-space-3);
520
+ }
521
+ }
522
+ /* A field asking for the whole row gets it in either arrangement. */
523
+ .sf-property-view-field--full {
524
+ grid-column: 1/-1;
525
+ }
526
+ _:-ms-input-placeholder, html:not([dir="rtl"]) .sf-property-view-required{
527
+ margin-left: var(--sf-space-1);
528
+ }
529
+ @supports not (margin-inline-start: initial){
530
+ html:not([dir="rtl"]) .sf-property-view-required{
531
+ margin-left: var(--sf-space-1);
532
+ }
533
+ }
534
+ .sf-property-view-required:where(:dir(ltr)){
535
+ margin-left: var(--sf-space-1);
536
+ }
537
+ _:-ms-input-placeholder, [dir="rtl"] .sf-property-view-required{
538
+ margin-right: var(--sf-space-1);
539
+ }
540
+ @supports not (margin-inline-start: initial){
541
+ [dir="rtl"] .sf-property-view-required{
542
+ margin-right: var(--sf-space-1);
543
+ }
544
+ }
545
+ .sf-property-view-required:where(:dir(rtl)){
546
+ margin-right: var(--sf-space-1);
547
+ }
548
+ .sf-property-view-required {
549
+ -webkit-margin-start: var(--sf-space-1);
550
+ margin-inline-start: var(--sf-space-1);
551
+ }
552
+ /* A field that asked for the whole row gets a control that fills it. The value
553
+ box is a flex row everywhere else, because a user value is an avatar beside a
554
+ name; a full-width field holds one control, and in a flex row that control
555
+ keeps its intrinsic width -- measured at 246px inside a 900px row. As a block
556
+ it fills: 900 of 900. The first attempt set `flex: 1 1 auto` on the box's
557
+ children, which reaches nothing, because a Smart element is display:contents
558
+ and its painted element is a grandchild rather than a flex item. */
559
+ .sf-property-view-field--full .sf-property-view-field-value {
560
+ display: block;
561
+ }
562
+ .sf-property-view-field-value {
563
+ /* A value may be text, a chip, an avatar and a name, or a control. They all
564
+ sit on one line with the label above, so the box is a flex row rather than
565
+ a block: a bare string and an element must align the same way. */
566
+ display: flex;
567
+ }
568
+ /* A datetime is a day and a time on one row (js/datetime-edit.js): the day
569
+ as wide as a date field is anywhere in the view, the time as wide as
570
+ «00:00» in its own font, so it follows the field's size step with the
571
+ datepicker. Both are the same size on the ladder, so they stand at one
572
+ height. The row does not wrap: a time under its day reads as a second
573
+ field. */
574
+ .sf-property-view-datetime {
575
+ display: flex;
576
+ align-items: center;
577
+ gap: var(--sf-space-2);
578
+ max-width: 100%;
579
+ max-inline-size: 100%;
580
+ min-width: 0;
581
+ min-inline-size: 0;
582
+ }
583
+ /* Both hosts are display: contents, so the flex items are what they draw:
584
+ the datepicker's wrap and the time field's root (its root-class). */
585
+ .sf-property-view-datetime > sf-input-date > .sf-datepicker-wrap {
586
+ flex: 0 1 auto;
587
+ min-width: 0;
588
+ min-inline-size: 0;
589
+ }
590
+ .sf-property-view-datetime .sf-property-view-datetime-time-field {
591
+ flex: 0 0 6.5em;
592
+ min-width: 0;
593
+ min-inline-size: 0;
594
+ }
595
+ /* A value whose editor does not exist yet is shown, not blanked, and reads as
596
+ not-yet-editable rather than as empty. */
597
+ /* The host's actions in the full-page layout. In a panel they sit beside the
598
+ panel's own three controls; here there is no header yet -- that arrives with
599
+ the header step -- so they get a row of their own above the body rather than
600
+ being panel-only, which is what was promised. */
601
+ .sf-property-view-host-actions {
602
+ margin-bottom: var(--sf-space-3);
603
+ -webkit-margin-after: var(--sf-space-3);
604
+ margin-block-end: var(--sf-space-3);
605
+ }
606
+ /* The host's own content, under the body and above the save bar. It gets a
607
+ place and a gap and nothing else: what a consumer puts here is theirs, and a
608
+ composite styling it would be deciding how someone else's menu looks. */
609
+ .sf-property-view-host-footer {
610
+ margin-top: var(--sf-space-3);
611
+ -webkit-margin-before: var(--sf-space-3);
612
+ margin-block-start: var(--sf-space-3);
613
+ }
614
+ /* An empty value reads as empty rather than as absent. */
615
+ /* The bar sticks to the bottom of the view, not of the viewport: this component
616
+ is also a panel and a phone screen, and a bar fixed to the viewport would
617
+ float over whatever is behind it. */
618
+ /* The floor of the panel, not a card standing on it: it reaches both edges and
619
+ the bottom, because a bar inset from the panel it belongs to wastes the
620
+ surface on three sides and reads as another card. The drawer's body carries
621
+ the inline gutter and the block end, so the bar takes them back. */
622
+ /* It holds the bottom of the panel and keeps the panel's whole width. The
623
+ drawer's panel is the scroller, so sticking to the bottom sticks to the
624
+ panel's own bottom, and the negative inline margins take back the gutter the
625
+ body applies. There is no scrollbar inside that gutter to sit in any more. */
626
+ /* The bar holds the foot of the panel whether or not there is enough to scroll
627
+ (owner, 2026-10-08): with little in the record it rose to meet the content
628
+ and stood in the middle of an empty panel. The auto margin takes the space
629
+ that is left, so an empty panel puts the bar where a full one does. */
630
+ .sf-property-view-actions {
631
+ flex: 0 0 auto;
632
+ z-index: 1;
633
+ margin-left: calc(-1 * var(--sf-property-view--panel-bleed));
634
+ margin-right: calc(-1 * var(--sf-property-view--panel-bleed));
635
+ margin-inline: calc(-1 * var(--sf-property-view--panel-bleed));
636
+ padding: var(--sf-space-1) var(--sf-property-view--panel-axis);
637
+ background-color: var(--sf-surface-2);
638
+ border-top: var(--sf-px) solid var(--sf-outline-variant);
639
+ }
640
+ /* Save and cancel hold the end of the bar; what is said about the edit, and
641
+ the record's own actions, hold the start. */
642
+ /* The group being edited says so, so a person can see which card the bar at the
643
+ bottom belongs to. */
644
+ .sf-property-view-group--editing {
645
+ border-color: var(--sf-primary);
646
+ }
647
+ /* The Framework's own md step, --sf-breakpoint-md: 48rem. A media query
648
+ cannot read a custom property, so the number is written out -- but it is
649
+ written the same way everywhere. The px form of this same boundary that
650
+ stood here parts company with the rem form as soon as the root is not
651
+ 16px, which is exactly what a consumer running at 14px does. */
652
+ @media (max-width: 47.99rem) {
653
+ /* The section list becomes a row above the fields rather than a column
654
+ beside them. The field columns are not here any more: they follow the
655
+ width of the panel through a container query, because a media query asks
656
+ the viewport and a panel is narrow inside a wide one. The request asks for
657
+ the section list to become its own screen on a phone, which arrives with
658
+ the phone step -- this keeps it usable until then rather than leaving a
659
+ 28-pixel column of truncated names. */
660
+ .sf-property-view-layout {
661
+ display: block;
662
+ }
663
+ .sf-property-view-sections {
664
+ margin-bottom: var(--sf-space-3);
665
+ -webkit-margin-after: var(--sf-space-3);
666
+ margin-block-end: var(--sf-space-3);
667
+ }
668
+ }
669
+ @media (forced-colors: active) {
670
+ .sf-property-view-group {
671
+ border-color: CanvasText;
672
+ }
673
+ .sf-property-view-actions {
674
+ border-top-color: CanvasText;
675
+ }
676
+ }
677
+ /* Configuring the view. Every rule here is scoped to its own class rather than
678
+ to the element, because a panel is portalled out of sf-view-properties and a
679
+ rule hung off the element would not reach it -- the mistake that produced
680
+ four of one consumer's seven layout reports at once. */
681
+ /* Arranging the view is a mode, so it is drawn as one: a tinted band that says
682
+ the panel is not being read right now. The colour is a container role rather
683
+ than a chosen tint, so it follows the theme into dark without a second
684
+ declaration.
685
+
686
+ Primary rather than warning (owner, 2026-10-07): arranging is not a warning,
687
+ and the container role is the one with a text colour of its own. The other
688
+ candidate, the primary overlay, is a state wash -- it has no paired text
689
+ role, so nothing would guarantee the contrast of the words on it. */
690
+ /* One row of controls: the layer being written, and the two ways out. It
691
+ carried a title, a sentence and two differently-shaped actions as well, and
692
+ read as three kinds of thing at once. */
693
+ /* In the foot the mode's controls are controls, not a band: the foot already
694
+ says where they are, and a tinted block inside it read as something stuck
695
+ on top of the panel (owner, 2026-10-08). */
696
+ /* The sentence tells a person what the mode lets them do and that it applies at
697
+ once. It takes the free space, so the two actions keep the end of the band. */
698
+ /* The grip is the handle sf-sortable looks for. It takes a column of the field
699
+ row rather than floating over it, so a label never sits underneath it. */
700
+ /* The handle carries its own size. It is drawn with an icon, and an icon has
701
+ no box until its font arrives -- so until then the handle was a point, and
702
+ a drag could not be started on it at all. */
703
+ .sf-property-view-grip {
704
+ flex: 0 0 auto;
705
+ display: inline-flex;
706
+ justify-content: center;
707
+ /* On the label's own step, with the two controls at the other end of the
708
+ row. At the control step it was the largest thing in a row of text. */
709
+ font-size: var(--sf-space-2);
710
+ font-size: var(--sf-text-height-1\/2, var(--sf-space-2));
711
+ width: var(--sf-space-2);
712
+ inline-size: var(--sf-space-2);
713
+ height: var(--sf-space-2);
714
+ block-size: var(--sf-space-2);
715
+ cursor: -webkit-grab;
716
+ cursor: grab;
717
+ /* Paler than the words beside it, by the role the Framework keeps for that
718
+ -- the same one a field's label reads in. Opacity was doing this before,
719
+ which pales a glyph by making it transparent: it reads as disabled, and
720
+ what shows through is whatever happens to be behind (owner,
721
+ 2026-10-08). */
722
+ /* Through the icon's own token, not `color` and not a colour utility: the
723
+ glyph reads `var(--sf-icon--color, currentColor)` in the components
724
+ layer, and utilities lose to components -- the handle carried
725
+ `color-on-surface-variant` and was drawn at the weight of the words
726
+ anyway. */
727
+ --sf-icon--color: var(--sf-on-surface-variant);
728
+ }
729
+ .sf-property-view-grip:hover {
730
+ --sf-icon--color: var(--sf-on-surface);
731
+ }
732
+ [data-sf-sortable-dragging] .sf-property-view-grip,
733
+ .sf-property-view-grip:active {
734
+ cursor: -webkit-grabbing;
735
+ cursor: grabbing;
736
+ }
737
+ /* The groups are one sortable list and the unplaced area stands after it, so a
738
+ group dropped at the end does not land past something that is not a group. */
739
+ /* The area for fields the view places nowhere. Dashed, because it is not a
740
+ card of the record: it is the edge of the arrangement, and it is drawn empty
741
+ so that a field dragged out of every group has somewhere to land. */
742
+ .sf-property-view-group--unplaced {
743
+ border-style: dashed;
744
+ background-color: transparent;
745
+ }
746
+ /* One row, nothing under it: the name and the handle only. */
747
+ .sf-property-view-field--unplaced {
748
+ grid-template-areas: "grip label actions";
749
+ }
750
+ /* An empty one still has to be a target a pointer can reach. */
751
+ .sf-property-view-unplaced {
752
+ min-height: var(--sf-space-4);
753
+ min-block-size: var(--sf-space-4);
754
+ }
755
+ /* A hidden entry stays visible while the view is being configured -- a hidden
756
+ field nobody can see is a field nobody can show again -- and is dimmed so the
757
+ difference is not something a person has to remember. */
758
+ .sf-property-view-field[data-hidden=true],
759
+ .sf-property-view-group[data-hidden=true] {
760
+ opacity: 0.55;
761
+ }
762
+ /* The two controls a field carries while the view is arranged sit at its end,
763
+ on the row the label starts, so the arrangement reads as a list of fields
764
+ rather than a list of controls. */
765
+ /* While the view is arranged a field is three things on one row -- the handle,
766
+ the label and the two controls -- with the value under them. As a column flex
767
+ the handle took a line of its own above the label and the controls took a line
768
+ under the value, which is three rows per field and the reason a group of eight
769
+ could no longer be read as an arrangement. */
770
+ .sf-property-view-field[data-arranging=true] {
771
+ display: grid;
772
+ grid-template-columns: auto minmax(0, 1fr) auto;
773
+ grid-template-areas: "grip label actions" ". value value";
774
+ align-items: center;
775
+ grid-column-gap: var(--sf-space-1);
776
+ -webkit-column-gap: var(--sf-space-1);
777
+ -moz-column-gap: var(--sf-space-1);
778
+ column-gap: var(--sf-space-1);
779
+ }
780
+ .sf-property-view-field[data-arranging=true] > .sf-property-view-grip {
781
+ grid-area: grip;
782
+ }
783
+ .sf-property-view-field[data-arranging=true] > .sf-property-view-field-label {
784
+ grid-area: label;
785
+ }
786
+ .sf-property-view-field[data-arranging=true] > .sf-property-view-field-value {
787
+ grid-area: value;
788
+ }
789
+ .sf-property-view-field-actions {
790
+ grid-area: actions;
791
+ }
792
+ /* The settings surface is anchored to the gear it opened from. It needs a width
793
+ of its own: a context menu sizes to its content, and a dropdown inside one
794
+ would otherwise set the width of the whole surface. */
795
+ /* The head's menu anchors to the «⋯» it opened from, the same way a field's
796
+ settings anchor to their gear. */
797
+ /* An inline view has no head, so its own row carries the «⋯» at the end. */
798
+ /* A rare entry is marked, not hidden from the person who chose to see it. */
799
+ .sf-property-view-rare {
800
+ font-size: var(--sf-label-small--size);
801
+ line-height: var(--sf-label-small--height);
802
+ opacity: 0.75;
803
+ }
804
+ /* The section being arranged, named above its areas. */
805
+ .sf-property-view-section-bar {
806
+ padding-bottom: var(--sf-space-1);
807
+ -webkit-padding-after: var(--sf-space-1);
808
+ padding-block-end: var(--sf-space-1);
809
+ border-bottom: var(--sf-px) solid var(--sf-outline-variant);
810
+ -webkit-border-after: var(--sf-px) solid var(--sf-outline-variant);
811
+ border-block-end: var(--sf-px) solid var(--sf-outline-variant);
812
+ }
813
+ .sf-property-view-section-title {
814
+ margin: 0;
815
+ font-size: var(--sf-title-small--size);
816
+ line-height: var(--sf-title-small--height);
817
+ }
818
+ /* A name that can be changed is a control, and says so only by being one: it
819
+ keeps the type of the title it replaces rather than borrowing a link's. */
820
+ .sf-property-view-rename-trigger {
821
+ -webkit-appearance: none;
822
+ -moz-appearance: none;
823
+ appearance: none;
824
+ border: 0;
825
+ margin: 0;
826
+ padding: 0;
827
+ background: none;
828
+ color: inherit;
829
+ font: inherit;
830
+ }
831
+ .sf-property-view-rename-trigger:hover {
832
+ text-decoration: underline;
833
+ }
834
+ /* The two answers to a name being typed stand beside the field, on the step
835
+ the rest of the arranging mode uses. */
836
+ .sf-property-view-additions {
837
+ padding-top: var(--sf-space-1);
838
+ -webkit-padding-before: var(--sf-space-1);
839
+ padding-block-start: var(--sf-space-1);
840
+ }
841
+ /* «Make a new box» stands the width of the arrangement it adds to. A 149px
842
+ button under a 525px column read as a control belonging to whatever sat
843
+ left of it rather than to the whole column (owner, 2026-10-09). */
844
+ .sf-property-view-additions sf-button,
845
+ .sf-property-view-additions .sf-button {
846
+ width: 100%;
847
+ inline-size: 100%;
848
+ }
849
+ /* The not-placed bin is the Framework's accordion, so its own chrome is the
850
+ accordion's. What is left here is what the arrangement needs: the rows
851
+ inside it stand closer than fields in an area do, because they are a list
852
+ of names rather than a form, and the bin keeps the dashed edge that says
853
+ it is where things land (owner, 2026-10-09). */
854
+ .sf-property-view-group--unplaced .sf-accordion-content {
855
+ padding-top: 0;
856
+ -webkit-padding-before: 0;
857
+ padding-block-start: 0;
858
+ }
859
+ .sf-property-view-unplaced {
860
+ gap: var(--sf-space-1\/3);
861
+ }
862
+ .sf-property-view-unplaced .sf-property-view-field--unplaced {
863
+ padding-top: var(--sf-space-1\/4);
864
+ padding-bottom: var(--sf-space-1\/4);
865
+ padding-block: var(--sf-space-1\/4);
866
+ }
867
+ .sf-property-view-settings-row--stacked {
868
+ align-items: stretch;
869
+ }
870
+ /* The settings read as a form of 1/2 controls, so their labels take the size
871
+ a 1/2 control gives its own label -- the text ladder's 1/3 step. They were
872
+ on the label-small role, which is a different ladder, and stood smaller
873
+ than anything beside them (owner, 2026-10-08). */
874
+ .sf-property-view-settings-label {
875
+ font-size: var(--sf-text-size-1\/3);
876
+ line-height: var(--sf-text-height-1\/3);
877
+ }
878
+ /* A related list's rows stand on the same inline axis as the field labels in the
879
+ groups around it. The table's own text sits a little in from its edge -- the
880
+ cell spacing before the first cell plus that cell's padding -- so the list is
881
+ pulled out by exactly that, using the number the table publishes rather than a
882
+ constant typed here, which would drift the first time either part changed.
883
+ Measured at 169.95px against labels at 57px before the two chrome columns went,
884
+ and at 36 against 33 after; this closes the last three pixels. */
885
+ _:-ms-input-placeholder, html:not([dir="rtl"]) .sf-property-view-group-related{
886
+ margin-left: calc(-1 * 0px);
887
+ margin-left: calc(-1 * var(--sf-table--row-text-inset, 0px));
888
+ }
889
+ @supports not (margin-inline-start: initial){
890
+ html:not([dir="rtl"]) .sf-property-view-group-related{
891
+ margin-left: calc(-1 * 0px);
892
+ margin-left: calc(-1 * var(--sf-table--row-text-inset, 0px));
893
+ }
894
+ }
895
+ .sf-property-view-group-related:where(:dir(ltr)){
896
+ margin-left: calc(-1 * 0px);
897
+ margin-left: calc(-1 * var(--sf-table--row-text-inset, 0px));
898
+ }
899
+ _:-ms-input-placeholder, [dir="rtl"] .sf-property-view-group-related{
900
+ margin-right: calc(-1 * 0px);
901
+ margin-right: calc(-1 * var(--sf-table--row-text-inset, 0px));
902
+ }
903
+ @supports not (margin-inline-start: initial){
904
+ [dir="rtl"] .sf-property-view-group-related{
905
+ margin-right: calc(-1 * 0px);
906
+ margin-right: calc(-1 * var(--sf-table--row-text-inset, 0px));
907
+ }
908
+ }
909
+ .sf-property-view-group-related:where(:dir(rtl)){
910
+ margin-right: calc(-1 * 0px);
911
+ margin-right: calc(-1 * var(--sf-table--row-text-inset, 0px));
912
+ }
913
+ .sf-property-view-group-related {
914
+ -webkit-margin-start: calc(-1 * 0px);
915
+ margin-inline-start: calc(-1 * 0px);
916
+ -webkit-margin-start: calc(-1 * var(--sf-table--row-text-inset, 0px));
917
+ margin-inline-start: calc(-1 * var(--sf-table--row-text-inset, 0px));
918
+ }
919
+ /* A record that holds nothing says so, rather than being an empty box that
920
+ reads as something still loading. */
921
+ .sf-property-view-empty-record {
922
+ margin: 0;
923
+ padding: var(--sf-space-4) var(--sf-space-2);
924
+ text-align: center;
925
+ }
926
+ .sf-property-view-group-error {
927
+ color: currentColor;
928
+ color: var(--sf-color-danger-text, currentColor);
929
+ padding-left: var(--sf-space-3);
930
+ padding-right: var(--sf-space-3);
931
+ padding-inline: var(--sf-space-3);
932
+ padding-bottom: var(--sf-space-2);
933
+ -webkit-padding-after: var(--sf-space-2);
934
+ padding-block-end: var(--sf-space-2);
935
+ }
936
+ /* Nothing restates the body or field-list layout for sf-sortable: it carries the
937
+ same class, and `sf-sortable { display: block }` is declared in sf.base while
938
+ these rules are in sf.components, which sits above it. The first draft of this
939
+ file duplicated both containers here with values typed from memory -- a gap of
940
+ space-3 where the body uses space-4 -- which would have reflowed the whole
941
+ arrangement at the moment a person entered the mode for arranging it. */
942
+ /* The phone's two screens. Below the drawer's own breakpoint the panel is full
943
+ screen, the sections are a list, and a section is the screen behind it. The
944
+ query matches sf-drawer's docking query rather than the breakpoint array,
945
+ because the panel is a drawer: flipping its arrangement 48px away from where
946
+ the drawer changes its own behaviour would be visible in that gap. */
947
+ .sf-property-view-phone-section {
948
+ min-height: var(--sf-ui-2--control-height);
949
+ min-block-size: var(--sf-ui-2--control-height);
950
+ padding-left: var(--sf-space-3);
951
+ padding-right: var(--sf-space-3);
952
+ padding-inline: var(--sf-space-3);
953
+ border: 0;
954
+ background: none;
955
+ color: inherit;
956
+ font: inherit;
957
+ }
958
+ .sf-property-view-phone-section + .sf-property-view-phone-section {
959
+ border-top: var(--sf-px) solid var(--sf-outline-variant);
960
+ -webkit-border-before: var(--sf-px) solid var(--sf-outline-variant);
961
+ border-block-start: var(--sf-px) solid var(--sf-outline-variant);
962
+ }
963
+ .sf-property-view-phone-head {
964
+ min-height: var(--sf-ui-2--control-height);
965
+ min-block-size: var(--sf-ui-2--control-height);
966
+ }
967
+ .sf-property-view-phone-title {
968
+ margin: 0;
969
+ font: inherit;
970
+ font-weight: 600;
971
+ }
972
+ @media (max-width: 47.99rem) {
973
+ /* The action bar holds the bottom of the screen, so Save stays reachable
974
+ while a long group scrolls past it. It is a row of the panel's own column
975
+ and the content scrolls between the bands, so it holds that place without
976
+ being positioned at all. */
977
+ .sf-property-view-actions {
978
+ /* The same surface the component paints. --sf-surface does not exist -- the
979
+ ladder is surface-0/1/2 -- and an empty var() would have left the bar
980
+ transparent, with the rows scrolling through the only control that saves
981
+ them. */
982
+ background-color: var(--sf-surface-0);
983
+ }
984
+ }
985
+ /* The panel's head: the collection on its own line, then the record's name with
986
+ its star and the «⋯». One block with the body below it, so nothing stands
987
+ between the title and the first card -- the view this replaces had two empty
988
+ bands there, which was the first thing the owner pointed at. */
989
+ /* The head is a band of its own (owner, 2026-10-08): it keeps its place while
990
+ the record scrolls under it and says where it ends with a rule, so the name
991
+ of the record and the first card are no longer one undivided stretch. The
992
+ rule reaches both edges, which is why the inline gutter is taken back here
993
+ and given again as padding -- the same thing the bar at the foot does. */
994
+ .sf-property-view-head {
995
+ flex: 0 0 auto;
996
+ z-index: 2;
997
+ margin-left: calc(-1 * var(--sf-property-view--panel-bleed));
998
+ margin-right: calc(-1 * var(--sf-property-view--panel-bleed));
999
+ margin-inline: calc(-1 * var(--sf-property-view--panel-bleed));
1000
+ padding: var(--sf-property-view--panel-bleed) var(--sf-property-view--panel-axis) var(--sf-space-1);
1001
+ background-color: var(--sf-surface-1);
1002
+ border-bottom: var(--sf-px) solid var(--sf-outline-variant);
1003
+ -webkit-border-after: var(--sf-px) solid var(--sf-outline-variant);
1004
+ border-block-end: var(--sf-px) solid var(--sf-outline-variant);
1005
+ }
1006
+ .sf-property-view-title {
1007
+ margin: 0;
1008
+ font-size: var(--sf-heading-4--size);
1009
+ font-size: var(--sf-property-view--title-size, var(--sf-heading-4--size));
1010
+ line-height: var(--sf-heading-4--height);
1011
+ line-height: var(--sf-property-view--title-height, var(--sf-heading-4--height));
1012
+ font-weight: 700;
1013
+ text-overflow: ellipsis;
1014
+ white-space: nowrap;
1015
+ }
1016
+ /* Beside the panel, not in it: the strip goes into the drawer's own edge area,
1017
+ which is a sibling of the panel and is not clipped by it. How the controls
1018
+ stack is said with utilities on the element; this only rounds them, because
1019
+ a control standing on a surface of its own is a round one. They carry that
1020
+ surface -- a transparent control with a dark glyph stood on the dimmed page
1021
+ and could not be seen at all, which is why the owner found no way to close
1022
+ the panel. */
1023
+ .sf-property-view-edge sf-button-icon button {
1024
+ border-radius: 50%;
1025
+ }
1026
+ /* A panel filling the screen has the strip inside it, in a row along its top
1027
+ at the drawer's inline step (space-1). The head starts its line after the
1028
+ strip and a step more, so the record's name is not under the controls
1029
+ (docs review, round 4, 2026-10-20). The strip's width is the template's:
1030
+ how many controls it carries times one control, and the steps between. */
1031
+ _:-ms-input-placeholder, html:not([dir="rtl"]) .sf-property-view-panel--fills .sf-property-view-head{
1032
+ padding-left: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1033
+ padding-left: calc(var(--sf-space-1) + var(--sf-property-view--edge-room, 0px) + var(--sf-space-1\/2));
1034
+ }
1035
+ @supports not (padding-inline-start: initial){
1036
+ html:not([dir="rtl"]) .sf-property-view-panel--fills .sf-property-view-head{
1037
+ padding-left: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1038
+ padding-left: calc(var(--sf-space-1) + var(--sf-property-view--edge-room, 0px) + var(--sf-space-1\/2));
1039
+ }
1040
+ }
1041
+ .sf-property-view-panel--fills .sf-property-view-head:where(:dir(ltr)){
1042
+ padding-left: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1043
+ padding-left: calc(var(--sf-space-1) + var(--sf-property-view--edge-room, 0px) + var(--sf-space-1\/2));
1044
+ }
1045
+ _:-ms-input-placeholder, [dir="rtl"] .sf-property-view-panel--fills .sf-property-view-head{
1046
+ padding-right: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1047
+ padding-right: calc(var(--sf-space-1) + var(--sf-property-view--edge-room, 0px) + var(--sf-space-1\/2));
1048
+ }
1049
+ @supports not (padding-inline-start: initial){
1050
+ [dir="rtl"] .sf-property-view-panel--fills .sf-property-view-head{
1051
+ padding-right: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1052
+ padding-right: calc(var(--sf-space-1) + var(--sf-property-view--edge-room, 0px) + var(--sf-space-1\/2));
1053
+ }
1054
+ }
1055
+ .sf-property-view-panel--fills .sf-property-view-head:where(:dir(rtl)){
1056
+ padding-right: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1057
+ padding-right: calc(var(--sf-space-1) + var(--sf-property-view--edge-room, 0px) + var(--sf-space-1\/2));
1058
+ }
1059
+ _:-ms-input-placeholder, html:not([dir="rtl"]) .sf-property-view-panel--fills .sf-property-view-head{
1060
+ padding-left: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1061
+ }
1062
+ @supports not (padding-inline-start: initial){
1063
+ html:not([dir="rtl"]) .sf-property-view-panel--fills .sf-property-view-head{
1064
+ padding-left: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1065
+ }
1066
+ }
1067
+ .sf-property-view-panel--fills .sf-property-view-head:where(:dir(ltr)){
1068
+ padding-left: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1069
+ }
1070
+ _:-ms-input-placeholder, [dir="rtl"] .sf-property-view-panel--fills .sf-property-view-head{
1071
+ padding-right: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1072
+ }
1073
+ @supports not (padding-inline-start: initial){
1074
+ [dir="rtl"] .sf-property-view-panel--fills .sf-property-view-head{
1075
+ padding-right: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1076
+ }
1077
+ }
1078
+ .sf-property-view-panel--fills .sf-property-view-head:where(:dir(rtl)){
1079
+ padding-right: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1080
+ }
1081
+ .sf-property-view-panel--fills .sf-property-view-head {
1082
+ -webkit-padding-start: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1083
+ padding-inline-start: calc(var(--sf-space-1) + 0px + var(--sf-space-1\/2));
1084
+ -webkit-padding-start: calc(var(--sf-space-1) + var(--sf-property-view--edge-room, 0px) + var(--sf-space-1\/2));
1085
+ padding-inline-start: calc(var(--sf-space-1) + var(--sf-property-view--edge-room, 0px) + var(--sf-space-1\/2));
1086
+ }
1087
+ /* A value is a button that opens its group, so reading and changing are one
1088
+ screen. It must still look like a value: no control chrome, the pen only when
1089
+ the pointer is on it or the keyboard is in it. */
1090
+ .sf-property-view-field-open {
1091
+ width: 100%;
1092
+ inline-size: 100%;
1093
+ padding: 0;
1094
+ border: 0;
1095
+ background: none;
1096
+ color: inherit;
1097
+ font: inherit;
1098
+ }
1099
+ /* The pen marks a value as a way in; it is not a control of its own, so it
1100
+ stands a step below the text it sits beside rather than at the icon size,
1101
+ where it was the largest thing in the row. */
1102
+ /* Shown on hover, and sized from the icon ladder rather than from a text
1103
+ height: a glyph beside a value is a 1/2 glyph (owner, 2026-10-08). */
1104
+ .sf-property-view-pen {
1105
+ opacity: 0;
1106
+ /* On the label's weight, not the value's: it is what can be done to the
1107
+ field, not what the field says. Through the icon's own token, for the
1108
+ reason the handle gives. */
1109
+ --sf-icon--color: var(--sf-on-surface-variant);
1110
+ }
1111
+ .sf-property-view-field-open:hover .sf-property-view-pen,
1112
+ .sf-property-view-field-open.focus-visible .sf-property-view-pen {
1113
+ opacity: 1;
1114
+ }
1115
+ .sf-property-view-field-open:hover .sf-property-view-pen,
1116
+ .sf-property-view-field-open:focus-visible .sf-property-view-pen {
1117
+ opacity: 1;
1118
+ }
1119
+ .sf-property-view-field-open.focus-visible {
1120
+ outline: var(--sf-focus--width) var(--sf-focus--style) var(--sf-focus--color);
1121
+ outline-offset: var(--sf-px);
1122
+ border-radius: var(--sf-radius-1);
1123
+ }
1124
+ .sf-property-view-field-open:focus-visible {
1125
+ outline: var(--sf-focus--width) var(--sf-focus--style) var(--sf-focus--color);
1126
+ outline-offset: var(--sf-px);
1127
+ border-radius: var(--sf-radius-1);
1128
+ }
1129
+ /* The hint is sf-help-mark now, not a glyph with a native title, so it carries
1130
+ no hover cursor of its own: what stands on the label's line is a button, and
1131
+ the mark's own stylesheet keeps it out of the line's height. The class is
1132
+ kept as the hook a probe and a consumer already address the hint by. */
1133
+ /* A block of code or markup kept in a file. Monospace and scrolling rather than
1134
+ wrapping, because a wrapped line of code is a line nobody can read; no syntax
1135
+ colouring, which needs an editor component and its own decision. */
1136
+ /* The Framework declares what code looks like -- family, surface and corner --
1137
+ and this reads those roles rather than naming its own. The family it named,
1138
+ --sf-font-family-mono, is not a token the theme has, so the block fell
1139
+ through to whatever monospace the browser keeps. What belongs to the field
1140
+ rather than to code is the height it is allowed and the scroll inside it. */
1141
+ .sf-field-include {
1142
+ margin: 0;
1143
+ max-height: calc(var(--sf-ui-2--control-height) * 6);
1144
+ max-block-size: calc(var(--sf-ui-2--control-height) * 6);
1145
+ overflow: auto;
1146
+ padding: var(--sf-space-2);
1147
+ border-radius: var(--sf-code--radius);
1148
+ background-color: var(--sf-code--background);
1149
+ color: var(--sf-code--color);
1150
+ font-family: var(--sf-code--font-family);
1151
+ white-space: pre;
1152
+ }
1153
+ }