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