janela 0.2.1 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +63 -1
- data/LICENSE.txt +1 -1
- data/README.md +244 -23
- data/UPGRADING.md +172 -0
- data/app/assets/javascripts/janela/chart_controller.js +23 -10
- data/app/assets/javascripts/janela/frame_controller.js +188 -0
- data/app/assets/javascripts/janela/vitral_controller.js +263 -0
- data/app/assets/stylesheets/janela.css +164 -0
- data/app/assets/stylesheets/vitral.css +343 -0
- data/app/controllers/janela/application_controller.rb +25 -0
- data/app/controllers/janela/frames_controller.rb +57 -0
- data/app/controllers/janela/panes_controller.rb +66 -13
- data/app/controllers/janela/queries_controller.rb +17 -0
- data/app/controllers/janela/{snapshot_panes_controller.rb → snapshot_queries_controller.rb} +7 -5
- data/app/helpers/janela/{dashboard_helper.rb → frames_helper.rb} +26 -9
- data/app/models/janela/frame.rb +28 -0
- data/app/models/janela/pane.rb +102 -99
- data/app/models/janela/query.rb +162 -0
- data/app/models/janela/snapshot.rb +5 -5
- data/app/views/janela/frames/_card.html.erb +7 -0
- data/app/views/janela/frames/_form.html.erb +23 -0
- data/app/views/janela/frames/_frame.html.erb +5 -0
- data/app/views/janela/frames/_pane.html.erb +12 -0
- data/app/views/janela/frames/edit.html.erb +25 -0
- data/app/views/janela/frames/index.html.erb +15 -0
- data/app/views/janela/frames/new.html.erb +5 -0
- data/app/views/janela/frames/show.html.erb +9 -0
- data/app/views/janela/panes/_form.html.erb +58 -0
- data/app/views/janela/panes/_row.html.erb +12 -0
- data/app/views/janela/panes/edit.html.erb +5 -0
- data/app/views/janela/panes/new.html.erb +22 -0
- data/app/views/janela/panes/show.html.erb +3 -46
- data/app/views/janela/queries/_query.html.erb +47 -0
- data/app/views/janela/queries/show.html.erb +4 -0
- data/app/views/janela/shared/_errors.html.erb +7 -0
- data/app/views/layouts/janela/application.html.erb +22 -5
- data/config/importmap.rb +2 -1
- data/config/locales/en.yml +65 -0
- data/config/routes.rb +15 -2
- data/db/migrate/20260916000001_create_janela_frames.rb +13 -0
- data/db/migrate/20260916000002_create_janela_panes.rb +22 -0
- data/docs/decisions/001-built-to-be-forked.md +4 -0
- data/docs/decisions/009-snapshots.md +5 -2
- data/docs/decisions/010-agent-guidance-ships-the-agent-waits.md +8 -4
- data/docs/decisions/012-frames-and-panes-are-data.md +166 -0
- data/docs/decisions/013-naming-and-addressing-frames.md +119 -0
- data/docs/decisions/014-corrections-before-frames-are-built.md +222 -0
- data/docs/decisions/015-how-breaking-change-is-communicated.md +114 -0
- data/docs/decisions/016-the-styling-vocabulary.md +100 -0
- data/docs/decisions/017-janela-owns-no-data-store.md +113 -0
- data/docs/decisions/018-a-table-is-the-universal-renderer.md +75 -0
- data/docs/decisions/019-a-created-frame-asks-the-host-who-owns-it.md +79 -0
- data/docs/decisions/020-formatting-belongs-to-the-measure.md +93 -0
- data/docs/decisions/021-a-check-has-a-name-a-host-can-silence.md +84 -0
- data/docs/decisions/022-host-route-helpers-work-inside-the-engine.md +104 -0
- data/docs/decisions/023-vitral-is-a-theme-not-the-stylesheet.md +86 -0
- data/docs/decisions/024-selecting-more-than-one-value.md +127 -0
- data/docs/decisions/INDEX.md +25 -7
- data/docs/multi-tenancy.md +175 -0
- data/docs/naming.md +172 -0
- data/lib/janela/definition.rb +5 -5
- data/lib/janela/doctor.rb +219 -0
- data/lib/janela/engine.rb +24 -2
- data/lib/janela/host_routes.rb +31 -0
- data/lib/janela/measure.rb +65 -4
- data/lib/janela/version.rb +1 -1
- data/lib/janela.rb +24 -0
- data/lib/tasks/janela.rake +6 -0
- metadata +57 -4
- data/app/assets/javascripts/janela/dashboard_controller.js +0 -58
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/* Janela's own styling, small on purpose and meant to be overridden
|
|
2
|
+
(ADR 016). A host includes it with stylesheet_link_tag "janela"; the engine
|
|
3
|
+
never injects it into a layout it does not own.
|
|
4
|
+
|
|
5
|
+
The names follow one rule, janela-{property}-{scale}, and the scale is
|
|
6
|
+
the integer stored on the record. An analyst's number chooses a rule that
|
|
7
|
+
is already written here rather than reaching CSS as a length, so there is
|
|
8
|
+
nothing to inject and nothing to escape. */
|
|
9
|
+
|
|
10
|
+
:root {
|
|
11
|
+
/* Retheme the whole spacing scale by setting this one value. */
|
|
12
|
+
--janela-space: 0.25rem;
|
|
13
|
+
--janela-line: rgba(128, 128, 128, 0.3);
|
|
14
|
+
--janela-accent: rgb(54, 162, 235);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.janela-frame { display: grid; }
|
|
18
|
+
|
|
19
|
+
/* A grid item that cannot shrink below its content overflows the column, and
|
|
20
|
+
a chart is exactly that. */
|
|
21
|
+
.janela-frame > * { min-width: 0; }
|
|
22
|
+
|
|
23
|
+
.janela-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
|
|
24
|
+
.janela-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
25
|
+
.janela-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
26
|
+
.janela-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
|
27
|
+
.janela-cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
|
28
|
+
.janela-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
|
|
29
|
+
.janela-cols-7 { grid-template-columns: repeat(7, minmax(0, 1fr)); }
|
|
30
|
+
.janela-cols-8 { grid-template-columns: repeat(8, minmax(0, 1fr)); }
|
|
31
|
+
.janela-cols-9 { grid-template-columns: repeat(9, minmax(0, 1fr)); }
|
|
32
|
+
.janela-cols-10 { grid-template-columns: repeat(10, minmax(0, 1fr)); }
|
|
33
|
+
.janela-cols-11 { grid-template-columns: repeat(11, minmax(0, 1fr)); }
|
|
34
|
+
.janela-cols-12 { grid-template-columns: repeat(12, minmax(0, 1fr)); }
|
|
35
|
+
|
|
36
|
+
.janela-gap-0 { gap: calc(var(--janela-space) * 0); }
|
|
37
|
+
.janela-gap-1 { gap: calc(var(--janela-space) * 1); }
|
|
38
|
+
.janela-gap-2 { gap: calc(var(--janela-space) * 2); }
|
|
39
|
+
.janela-gap-3 { gap: calc(var(--janela-space) * 3); }
|
|
40
|
+
.janela-gap-4 { gap: calc(var(--janela-space) * 4); }
|
|
41
|
+
.janela-gap-5 { gap: calc(var(--janela-space) * 5); }
|
|
42
|
+
.janela-gap-6 { gap: calc(var(--janela-space) * 6); }
|
|
43
|
+
.janela-gap-7 { gap: calc(var(--janela-space) * 7); }
|
|
44
|
+
.janela-gap-8 { gap: calc(var(--janela-space) * 8); }
|
|
45
|
+
|
|
46
|
+
.janela-span-1 { grid-column: span 1; }
|
|
47
|
+
.janela-span-2 { grid-column: span 2; }
|
|
48
|
+
.janela-span-3 { grid-column: span 3; }
|
|
49
|
+
.janela-span-4 { grid-column: span 4; }
|
|
50
|
+
.janela-span-5 { grid-column: span 5; }
|
|
51
|
+
.janela-span-6 { grid-column: span 6; }
|
|
52
|
+
.janela-span-7 { grid-column: span 7; }
|
|
53
|
+
.janela-span-8 { grid-column: span 8; }
|
|
54
|
+
.janela-span-9 { grid-column: span 9; }
|
|
55
|
+
.janela-span-10 { grid-column: span 10; }
|
|
56
|
+
.janela-span-11 { grid-column: span 11; }
|
|
57
|
+
.janela-span-12 { grid-column: span 12; }
|
|
58
|
+
|
|
59
|
+
/* A dashboard nobody can read on a phone is not a choice worth offering, so
|
|
60
|
+
the collapse is here rather than in the data (ADR 016). */
|
|
61
|
+
@media (max-width: 40rem) {
|
|
62
|
+
.janela-frame { grid-template-columns: minmax(0, 1fr); }
|
|
63
|
+
.janela-frame > * { grid-column: span 1; }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Enough base styling that a pane is legible on install (#15). Without it a
|
|
67
|
+
table pane runs its label into its number. */
|
|
68
|
+
.janela-value { margin: 0; display: flex; flex-direction: column; gap: calc(var(--janela-space) * 1); }
|
|
69
|
+
.janela-value-label { font-size: 0.85rem; opacity: 0.7; }
|
|
70
|
+
.janela-value-number { font-size: 2rem; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
71
|
+
|
|
72
|
+
table.janela-pane { width: 100%; border-collapse: collapse; }
|
|
73
|
+
table.janela-pane caption { text-align: left; font-weight: 600; margin-bottom: calc(var(--janela-space) * 2); }
|
|
74
|
+
table.janela-pane td { padding: calc(var(--janela-space) * 1.5) 0; border-top: 1px solid var(--janela-line); }
|
|
75
|
+
table.janela-pane td:last-child { text-align: right; font-variant-numeric: tabular-nums; }
|
|
76
|
+
table.janela-pane button {
|
|
77
|
+
font: inherit;
|
|
78
|
+
color: inherit;
|
|
79
|
+
background: none;
|
|
80
|
+
border: 0;
|
|
81
|
+
padding: calc(var(--janela-space) * 0.5) calc(var(--janela-space) * 2);
|
|
82
|
+
border-radius: 999px;
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
}
|
|
85
|
+
table.janela-pane button:hover { background: var(--janela-line); }
|
|
86
|
+
table.janela-pane button[aria-pressed="true"] { background: var(--janela-accent); color: white; }
|
|
87
|
+
|
|
88
|
+
canvas.janela-chart { width: 100% !important; max-height: 20rem; }
|
|
89
|
+
|
|
90
|
+
/* Janela's own pages (ADR 013). Scoped to a class the engine's layout sets,
|
|
91
|
+
so including this stylesheet changes nothing about a host's own pages. */
|
|
92
|
+
.janela-page {
|
|
93
|
+
max-width: 70rem;
|
|
94
|
+
margin: 0 auto;
|
|
95
|
+
padding: calc(var(--janela-space) * 6);
|
|
96
|
+
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
97
|
+
}
|
|
98
|
+
.janela-heading { margin: 0 0 calc(var(--janela-space) * 2); font-size: 1.5rem; }
|
|
99
|
+
.janela-crumb { margin: 0 0 calc(var(--janela-space) * 6); font-size: 0.875rem; opacity: 0.7; }
|
|
100
|
+
.janela-exit { margin: 0 0 calc(var(--janela-space) * 6); font-size: 0.875rem; opacity: 0.7; }
|
|
101
|
+
.janela-card {
|
|
102
|
+
display: flex;
|
|
103
|
+
flex-direction: column;
|
|
104
|
+
gap: var(--janela-space);
|
|
105
|
+
padding: calc(var(--janela-space) * 4);
|
|
106
|
+
border: 1px solid var(--janela-line);
|
|
107
|
+
border-radius: calc(var(--janela-space) * 2);
|
|
108
|
+
color: inherit;
|
|
109
|
+
text-decoration: none;
|
|
110
|
+
}
|
|
111
|
+
.janela-card:hover { border-color: var(--janela-accent); }
|
|
112
|
+
.janela-card-name { font-weight: 600; }
|
|
113
|
+
.janela-card-meta { font-size: 0.875rem; opacity: 0.7; }
|
|
114
|
+
.janela-subheading { margin: calc(var(--janela-space) * 8) 0 calc(var(--janela-space) * 2); font-size: 1.125rem; }
|
|
115
|
+
.janela-muted, .janela-hint { font-size: 0.875rem; opacity: 0.7; }
|
|
116
|
+
.janela-flash {
|
|
117
|
+
margin: 0 0 calc(var(--janela-space) * 4);
|
|
118
|
+
padding: calc(var(--janela-space) * 3);
|
|
119
|
+
border-radius: calc(var(--janela-space) * 2);
|
|
120
|
+
background: var(--janela-line);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* The analyst's editing surface. Plain HTML controls, because these pages run
|
|
124
|
+
no JavaScript (ADR 018): every form submits and every button is a button. */
|
|
125
|
+
.janela-form { display: flex; flex-direction: column; gap: calc(var(--janela-space) * 4); max-width: 30rem; }
|
|
126
|
+
.janela-field { display: flex; flex-direction: column; gap: var(--janela-space); }
|
|
127
|
+
.janela-field label { font-size: 0.875rem; font-weight: 600; }
|
|
128
|
+
.janela-field input, .janela-field select {
|
|
129
|
+
font: inherit;
|
|
130
|
+
padding: calc(var(--janela-space) * 2);
|
|
131
|
+
border: 1px solid var(--janela-line);
|
|
132
|
+
border-radius: calc(var(--janela-space) * 1.5);
|
|
133
|
+
background: transparent;
|
|
134
|
+
color: inherit;
|
|
135
|
+
}
|
|
136
|
+
.janela-actions { display: flex; flex-wrap: wrap; gap: calc(var(--janela-space) * 3); align-items: center; }
|
|
137
|
+
.janela-actions form { display: inline; }
|
|
138
|
+
.janela-button {
|
|
139
|
+
font: inherit;
|
|
140
|
+
padding: calc(var(--janela-space) * 2) calc(var(--janela-space) * 4);
|
|
141
|
+
border: 1px solid var(--janela-line);
|
|
142
|
+
border-radius: calc(var(--janela-space) * 1.5);
|
|
143
|
+
background: transparent;
|
|
144
|
+
color: inherit;
|
|
145
|
+
text-decoration: none;
|
|
146
|
+
cursor: pointer;
|
|
147
|
+
}
|
|
148
|
+
.janela-button:hover { border-color: var(--janela-accent); }
|
|
149
|
+
.janela-button[disabled] { opacity: 0.4; cursor: default; }
|
|
150
|
+
.janela-danger { color: #b3261e; }
|
|
151
|
+
.janela-errors { margin: 0; padding-left: calc(var(--janela-space) * 5); color: #b3261e; }
|
|
152
|
+
.janela-list { list-style: none; margin: 0; padding: 0; }
|
|
153
|
+
.janela-list-row {
|
|
154
|
+
display: flex;
|
|
155
|
+
flex-wrap: wrap;
|
|
156
|
+
gap: calc(var(--janela-space) * 3);
|
|
157
|
+
align-items: center;
|
|
158
|
+
padding: calc(var(--janela-space) * 3) 0;
|
|
159
|
+
border-top: 1px solid var(--janela-line);
|
|
160
|
+
}
|
|
161
|
+
.janela-list-name { flex: 1 1 12rem; font-weight: 600; }
|
|
162
|
+
|
|
163
|
+
.janela-empty { margin: 0; opacity: 0.7; }
|
|
164
|
+
.janela-error { margin: 0; }
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/* Vitral: Janela's optional theme.
|
|
2
|
+
|
|
3
|
+
A vitral is a stained glass window, which is what this makes of a dashboard.
|
|
4
|
+
Each pane holds its own colour, dark leading runs between them, and the
|
|
5
|
+
light comes from behind rather than from a lamp in the room.
|
|
6
|
+
|
|
7
|
+
It is separate from janela.css on purpose. That one is structure: the grid
|
|
8
|
+
classes an analyst's numbers choose from, and enough style that a pane is
|
|
9
|
+
legible on install. This one is taste, and taste is the first thing a host
|
|
10
|
+
will want to replace (ADR 023).
|
|
11
|
+
|
|
12
|
+
<%= stylesheet_link_tag "janela" %>
|
|
13
|
+
<%= stylesheet_link_tag "vitral" %>
|
|
14
|
+
|
|
15
|
+
Order matters: vitral is written to sit on top of janela. Everything here
|
|
16
|
+
is a custom property or a janela-* class, so it touches nothing of yours.
|
|
17
|
+
Set the properties on :root to retheme it without editing this file, and
|
|
18
|
+
put class="vitral" on the element that should carry the light. */
|
|
19
|
+
|
|
20
|
+
:root {
|
|
21
|
+
--vitral-ink: #16202b;
|
|
22
|
+
--vitral-muted: #4a5766;
|
|
23
|
+
|
|
24
|
+
/* The light behind the window. */
|
|
25
|
+
--vitral-light-cobalt: rgba(54, 122, 235, 0.34);
|
|
26
|
+
--vitral-light-teal: rgba(30, 170, 160, 0.26);
|
|
27
|
+
--vitral-light-amber: rgba(242, 178, 74, 0.26);
|
|
28
|
+
--vitral-light-rose: rgba(226, 98, 132, 0.20);
|
|
29
|
+
--vitral-light-violet: rgba(138, 110, 228, 0.22);
|
|
30
|
+
--vitral-ground: #e3eaf3;
|
|
31
|
+
/* The sun outside the window, the shafts it throws across the room, and
|
|
32
|
+
the ghosts a lens makes of it. */
|
|
33
|
+
--vitral-flare: rgba(255, 252, 244, 0.7);
|
|
34
|
+
--vitral-sun-x: 88vw;
|
|
35
|
+
--vitral-sun-y: -2vh;
|
|
36
|
+
--vitral-shaft: rgba(255, 253, 248, 0.92);
|
|
37
|
+
|
|
38
|
+
/* The glass, one hue per pane, laid over white so that only the surface
|
|
39
|
+
takes the colour and the text keeps its contrast. */
|
|
40
|
+
--vitral-pane-1: rgba(54, 122, 235, 0.32);
|
|
41
|
+
--vitral-pane-2: rgba(28, 168, 158, 0.30);
|
|
42
|
+
--vitral-pane-3: rgba(240, 176, 70, 0.34);
|
|
43
|
+
--vitral-pane-4: rgba(224, 96, 130, 0.28);
|
|
44
|
+
--vitral-pane-5: rgba(138, 110, 228, 0.30);
|
|
45
|
+
|
|
46
|
+
--vitral-glass: rgba(255, 255, 255, 0.55);
|
|
47
|
+
--vitral-glass-strong: rgba(255, 255, 255, 0.78);
|
|
48
|
+
--vitral-blur: saturate(160%) blur(14px);
|
|
49
|
+
|
|
50
|
+
/* Came: the dark lead that holds each pane. */
|
|
51
|
+
--vitral-came: rgba(26, 38, 54, 0.38);
|
|
52
|
+
--vitral-came-soft: rgba(26, 38, 54, 0.22);
|
|
53
|
+
|
|
54
|
+
/* The lead's own colour, and how much of it survives behind the reading
|
|
55
|
+
column. Both deliberately faint: this is texture at the edge of sight,
|
|
56
|
+
and it competes with the text the moment it is anything more. */
|
|
57
|
+
--vitral-lattice-ink: rgba(44, 62, 84, 0.14);
|
|
58
|
+
/* Held back only where the words are, which on a wide screen is the left of
|
|
59
|
+
the reading column rather than the middle of the window. The dip used to
|
|
60
|
+
span the whole centre and took the pattern out of a large area that has
|
|
61
|
+
nothing in it to clash with. */
|
|
62
|
+
--vitral-lattice-mask: linear-gradient(90deg,
|
|
63
|
+
#000 0 10%, rgba(0, 0, 0, 0.42) 20%, rgba(0, 0, 0, 0.42) 46%, #000 62% 100%);
|
|
64
|
+
|
|
65
|
+
--vitral-radius: 10px;
|
|
66
|
+
--vitral-shadow: 0 1px 2px rgba(16, 24, 40, 0.05), 0 16px 34px -22px rgba(16, 24, 40, 0.45);
|
|
67
|
+
|
|
68
|
+
/* Janela's own two properties, restated in the theme's terms so the parts
|
|
69
|
+
janela.css draws agree with the parts this one does. */
|
|
70
|
+
--janela-line: var(--vitral-came-soft);
|
|
71
|
+
--janela-accent: rgb(40, 110, 205);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Opt in, because a theme that repaints a host's <body> uninvited is not a
|
|
75
|
+
theme, it is a takeover. Janela's own pages carry it themselves. */
|
|
76
|
+
.vitral, .janela-page {
|
|
77
|
+
color: var(--vitral-ink);
|
|
78
|
+
background-color: var(--vitral-ground);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.vitral, body:has(> .janela-page) { min-height: 100vh; }
|
|
82
|
+
|
|
83
|
+
.vitral::before, .janela-page::before {
|
|
84
|
+
content: "";
|
|
85
|
+
position: fixed;
|
|
86
|
+
inset: -20vmax;
|
|
87
|
+
z-index: -2;
|
|
88
|
+
background:
|
|
89
|
+
/* Shafts of light, angled and parallel, the way a leadlight window throws
|
|
90
|
+
them across a floor. A linear gradient is already a band at an angle,
|
|
91
|
+
so no rotated element is needed for it. Soft stops on both sides,
|
|
92
|
+
because a hard edge reads as a graphic rather than as light. */
|
|
93
|
+
/* Shafts of light, angled and parallel, the way a leadlight window throws
|
|
94
|
+
them across a floor. A band is perpendicular to its gradient's angle, so
|
|
95
|
+
falling from the top right means an axis pointing down and right. */
|
|
96
|
+
linear-gradient(135deg,
|
|
97
|
+
transparent 36%, rgba(255, 252, 246, 0.22) 41%, var(--vitral-shaft) 45.5%,
|
|
98
|
+
rgba(255, 252, 246, 0.3) 49%, transparent 55%),
|
|
99
|
+
linear-gradient(135deg,
|
|
100
|
+
transparent 54%, rgba(255, 252, 247, 0.55) 58%, rgba(255, 252, 247, 0.16) 61%, transparent 66%),
|
|
101
|
+
linear-gradient(135deg,
|
|
102
|
+
transparent 64%, rgba(255, 251, 245, 0.34) 68%, transparent 74%),
|
|
103
|
+
/* A cooler band between the shafts, because light is only bright next to
|
|
104
|
+
something that is not. */
|
|
105
|
+
linear-gradient(135deg,
|
|
106
|
+
transparent 49%, rgba(104, 128, 166, 0.14) 52.5%, transparent 56%),
|
|
107
|
+
|
|
108
|
+
/* The flare, first so it sits over the colour rather than under it.
|
|
109
|
+
This layer hangs 20vmax outside the window on every side, so a
|
|
110
|
+
percentage here is not a percentage of the screen. Positions are given
|
|
111
|
+
in screen units and shifted by the overhang, which is why every one of
|
|
112
|
+
them carries the same + 20vmax. */
|
|
113
|
+
radial-gradient(4vmax 4vmax at calc(var(--vitral-sun-x) + 20vmax) calc(var(--vitral-sun-y) + 20vmax),
|
|
114
|
+
rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.5) 42%, transparent 66%),
|
|
115
|
+
radial-gradient(22vmax 22vmax at calc(var(--vitral-sun-x) + 20vmax) calc(var(--vitral-sun-y) + 20vmax),
|
|
116
|
+
var(--vitral-flare), rgba(255, 250, 238, 0.24) 40%, transparent 64%),
|
|
117
|
+
radial-gradient(4vmax 4vmax at calc(68vw + 20vmax) calc(24vh + 20vmax),
|
|
118
|
+
rgba(255, 236, 198, 0.42), transparent 62%),
|
|
119
|
+
radial-gradient(2.4vmax 2.4vmax at calc(60vw + 20vmax) calc(35vh + 20vmax),
|
|
120
|
+
rgba(198, 226, 255, 0.4), transparent 64%),
|
|
121
|
+
radial-gradient(6vmax 6vmax at calc(47vw + 20vmax) calc(50vh + 20vmax),
|
|
122
|
+
rgba(255, 224, 212, 0.26), transparent 66%),
|
|
123
|
+
radial-gradient(1.8vmax 1.8vmax at calc(37vw + 20vmax) calc(64vh + 20vmax),
|
|
124
|
+
rgba(206, 238, 222, 0.34), transparent 62%),
|
|
125
|
+
radial-gradient(34vmax 34vmax at 11% 6%, var(--vitral-light-cobalt), transparent 58%),
|
|
126
|
+
radial-gradient(22vmax 22vmax at 38% 27%, var(--vitral-light-teal), transparent 55%),
|
|
127
|
+
radial-gradient(30vmax 30vmax at 83% 14%, var(--vitral-light-rose), transparent 56%),
|
|
128
|
+
radial-gradient(26vmax 26vmax at 64% 47%, var(--vitral-light-violet), transparent 58%),
|
|
129
|
+
radial-gradient(38vmax 38vmax at 91% 69%, var(--vitral-light-amber), transparent 60%),
|
|
130
|
+
radial-gradient(28vmax 28vmax at 23% 81%, var(--vitral-light-teal), transparent 58%),
|
|
131
|
+
radial-gradient(24vmax 24vmax at 57% 96%, var(--vitral-light-cobalt), transparent 55%);
|
|
132
|
+
/* The far layer of the parallax: light moves with the pointer, lead moves
|
|
133
|
+
against it, which is what gives the glass its depth. Both sit still until
|
|
134
|
+
something sets the properties, so this is nothing without the controller
|
|
135
|
+
and nothing for anyone who asked for less motion. */
|
|
136
|
+
transform: translate3d(calc(var(--vitral-shift-x, 0) * 26px),
|
|
137
|
+
calc(var(--vitral-shift-y, 0) * 26px + var(--vitral-drift-far, 0px)), 0);
|
|
138
|
+
transition: transform .45s cubic-bezier(.2, .7, .3, 1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* The leadlight itself: cut once with a seeded random hand, so no two cells
|
|
142
|
+
are the same shape and nothing repeats across the viewport. Inlined rather
|
|
143
|
+
than shipped as an image, because a stylesheet in a gem cannot rely on a
|
|
144
|
+
host's asset pipeline to resolve a url().
|
|
145
|
+
|
|
146
|
+
Masked away from the middle, which is the part of a real window you put
|
|
147
|
+
clear glass in so that you can see out of it. Text sits there; the leading
|
|
148
|
+
stays in the margins where it belongs. */
|
|
149
|
+
.vitral::after, .janela-page::after {
|
|
150
|
+
content: "";
|
|
151
|
+
position: fixed;
|
|
152
|
+
inset: -6vmax;
|
|
153
|
+
z-index: -1;
|
|
154
|
+
pointer-events: none;
|
|
155
|
+
opacity: 0.5;
|
|
156
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1600 1200' preserveAspectRatio='xMidYMid slice'%3E%3Cg stroke='rgba(44,62,84,0.11)' stroke-width='1' stroke-linejoin='round'%3E%3Cpolygon points='0.0,0.0 66.9,0.0 78.6,121.4 0.0,98.3' fill='none'/%3E%3Cpolygon points='66.9,0.0 192.5,0.0 178.8,94.6 78.6,121.4' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='192.5,0.0 290.9,0.0 305.4,64.6' fill='none'/%3E%3Cpolygon points='192.5,0.0 305.4,64.6 178.8,94.6' fill='none'/%3E%3Cpolygon points='290.9,0.0 350.1,0.0 363.5,120.0 305.4,64.6' fill='none'/%3E%3Cpolygon points='350.1,0.0 494.7,0.0 363.5,120.0' fill='none'/%3E%3Cpolygon points='494.7,0.0 457.4,69.4 363.5,120.0' fill='none'/%3E%3Cpolygon points='494.7,0.0 575.6,0.0 457.4,69.4' fill='none'/%3E%3Cpolygon points='575.6,0.0 554.0,78.0 457.4,69.4' fill='none'/%3E%3Cpolygon points='575.6,0.0 637.9,0.0 676.6,71.5 554.0,78.0' fill='none'/%3E%3Cpolygon points='637.9,0.0 776.8,0.0 722.9,91.5 676.6,71.5' fill='none'/%3E%3Cpolygon points='776.8,0.0 846.7,0.0 823.2,107.7' fill='none'/%3E%3Cpolygon points='776.8,0.0 823.2,107.7 722.9,91.5' fill='none'/%3E%3Cpolygon points='846.7,0.0 966.9,0.0 823.2,107.7' fill='none'/%3E%3Cpolygon points='966.9,0.0 932.0,109.5 823.2,107.7' fill='none'/%3E%3Cpolygon points='966.9,0.0 1063.4,0.0 1024.1,73.2 932.0,109.5' fill='none'/%3E%3Cpolygon points='1063.4,0.0 1104.8,0.0 1158.1,71.1 1024.1,73.2' fill='none'/%3E%3Cpolygon points='1104.8,0.0 1226.9,0.0 1198.1,64.3 1158.1,71.1' fill='none'/%3E%3Cpolygon points='1226.9,0.0 1339.6,0.0 1198.1,64.3' fill='none'/%3E%3Cpolygon points='1339.6,0.0 1313.3,67.5 1198.1,64.3' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1339.6,0.0 1386.6,0.0 1426.1,85.2' fill='none'/%3E%3Cpolygon points='1339.6,0.0 1426.1,85.2 1313.3,67.5' fill='none'/%3E%3Cpolygon points='1386.6,0.0 1501.7,0.0 1511.4,112.5 1426.1,85.2' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1501.7,0.0 1600.0,0.0 1600.0,98.7 1511.4,112.5' fill='none'/%3E%3Cpolygon points='0.0,98.3 78.6,121.4 0.0,176.0' fill='none'/%3E%3Cpolygon points='78.6,121.4 84.3,182.0 0.0,176.0' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='78.6,121.4 178.8,94.6 166.6,196.7 84.3,182.0' fill='none'/%3E%3Cpolygon points='178.8,94.6 305.4,64.6 309.4,174.0 166.6,196.7' fill='none'/%3E%3Cpolygon points='305.4,64.6 363.5,120.0 309.4,174.0' fill='none'/%3E%3Cpolygon points='363.5,120.0 384.6,204.4 309.4,174.0' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='363.5,120.0 457.4,69.4 476.7,177.4 384.6,204.4' fill='none'/%3E%3Cpolygon points='457.4,69.4 554.0,78.0 476.7,177.4' fill='none'/%3E%3Cpolygon points='554.0,78.0 554.0,189.5 476.7,177.4' fill='none'/%3E%3Cpolygon points='554.0,78.0 676.6,71.5 675.8,168.7' fill='none'/%3E%3Cpolygon points='554.0,78.0 675.8,168.7 554.0,189.5' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='676.6,71.5 722.9,91.5 774.9,166.4' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='676.6,71.5 774.9,166.4 675.8,168.7' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='722.9,91.5 823.2,107.7 859.6,171.5' fill='none'/%3E%3Cpolygon points='722.9,91.5 859.6,171.5 774.9,166.4' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='823.2,107.7 932.0,109.5 923.8,164.1 859.6,171.5' fill='none'/%3E%3Cpolygon points='932.0,109.5 1024.1,73.2 1044.8,185.9 923.8,164.1' fill='none'/%3E%3Cpolygon points='1024.1,73.2 1158.1,71.1 1143.2,205.1 1044.8,185.9' fill='none'/%3E%3Cpolygon points='1158.1,71.1 1198.1,64.3 1226.6,187.2 1143.2,205.1' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='1198.1,64.3 1313.3,67.5 1328.8,197.5 1226.6,187.2' fill='none'/%3E%3Cpolygon points='1313.3,67.5 1426.1,85.2 1384.3,200.6' fill='none'/%3E%3Cpolygon points='1313.3,67.5 1384.3,200.6 1328.8,197.5' fill='none'/%3E%3Cpolygon points='1426.1,85.2 1511.4,112.5 1527.8,181.8 1384.3,200.6' fill='none'/%3E%3Cpolygon points='1511.4,112.5 1600.0,98.7 1600.0,165.0' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='1511.4,112.5 1600.0,165.0 1527.8,181.8' fill='none'/%3E%3Cpolygon points='0.0,176.0 84.3,182.0 0.0,269.6' fill='none'/%3E%3Cpolygon points='84.3,182.0 121.3,291.5 0.0,269.6' fill='none'/%3E%3Cpolygon points='84.3,182.0 166.6,196.7 160.3,281.3 121.3,291.5' fill='none'/%3E%3Cpolygon points='166.6,196.7 309.4,174.0 287.4,278.9' fill='none'/%3E%3Cpolygon points='166.6,196.7 287.4,278.9 160.3,281.3' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='309.4,174.0 384.6,204.4 389.5,304.1 287.4,278.9' fill='none'/%3E%3Cpolygon points='384.6,204.4 476.7,177.4 474.3,256.5 389.5,304.1' fill='none'/%3E%3Cpolygon points='476.7,177.4 554.0,189.5 569.7,251.7 474.3,256.5' fill='none'/%3E%3Cpolygon points='554.0,189.5 675.8,168.7 672.5,283.9 569.7,251.7' fill='none'/%3E%3Cpolygon points='675.8,168.7 774.9,166.4 734.9,272.3 672.5,283.9' fill='none'/%3E%3Cpolygon points='774.9,166.4 859.6,171.5 846.6,287.4 734.9,272.3' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='859.6,171.5 923.8,164.1 963.1,257.5 846.6,287.4' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='923.8,164.1 1044.8,185.9 1060.5,253.6 963.1,257.5' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1044.8,185.9 1143.2,205.1 1117.1,289.7' fill='none'/%3E%3Cpolygon points='1044.8,185.9 1117.1,289.7 1060.5,253.6' fill='none'/%3E%3Cpolygon points='1143.2,205.1 1226.6,187.2 1117.1,289.7' fill='none'/%3E%3Cpolygon points='1226.6,187.2 1230.4,286.1 1117.1,289.7' fill='none'/%3E%3Cpolygon points='1226.6,187.2 1328.8,197.5 1343.4,275.3 1230.4,286.1' fill='none'/%3E%3Cpolygon points='1328.8,197.5 1384.3,200.6 1343.4,275.3' fill='none'/%3E%3Cpolygon points='1384.3,200.6 1383.9,263.9 1343.4,275.3' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1384.3,200.6 1527.8,181.8 1482.4,282.6 1383.9,263.9' fill='none'/%3E%3Cpolygon points='1527.8,181.8 1600.0,165.0 1600.0,268.9 1482.4,282.6' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='0.0,269.6 121.3,291.5 113.1,370.3 0.0,344.0' fill='none'/%3E%3Cpolygon points='121.3,291.5 160.3,281.3 188.0,371.8 113.1,370.3' fill='none'/%3E%3Cpolygon points='160.3,281.3 287.4,278.9 282.9,349.5' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='160.3,281.3 282.9,349.5 188.0,371.8' fill='none'/%3E%3Cpolygon points='287.4,278.9 389.5,304.1 403.9,354.6 282.9,349.5' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='389.5,304.1 474.3,256.5 452.9,358.5 403.9,354.6' fill='none'/%3E%3Cpolygon points='474.3,256.5 569.7,251.7 547.1,374.2 452.9,358.5' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='569.7,251.7 672.5,283.9 547.1,374.2' fill='none'/%3E%3Cpolygon points='672.5,283.9 664.3,359.3 547.1,374.2' fill='none'/%3E%3Cpolygon points='672.5,283.9 734.9,272.3 765.0,342.7 664.3,359.3' fill='none'/%3E%3Cpolygon points='734.9,272.3 846.6,287.4 822.4,394.5 765.0,342.7' fill='none'/%3E%3Cpolygon points='846.6,287.4 963.1,257.5 915.0,368.7' fill='none'/%3E%3Cpolygon points='846.6,287.4 915.0,368.7 822.4,394.5' fill='none'/%3E%3Cpolygon points='963.1,257.5 1060.5,253.6 1030.4,358.6' fill='none'/%3E%3Cpolygon points='963.1,257.5 1030.4,358.6 915.0,368.7' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1060.5,253.6 1117.1,289.7 1150.6,379.6 1030.4,358.6' fill='none'/%3E%3Cpolygon points='1117.1,289.7 1230.4,286.1 1208.7,362.3 1150.6,379.6' fill='none'/%3E%3Cpolygon points='1230.4,286.1 1343.4,275.3 1208.7,362.3' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='1343.4,275.3 1345.7,347.6 1208.7,362.3' fill='none'/%3E%3Cpolygon points='1343.4,275.3 1383.9,263.9 1416.4,374.4 1345.7,347.6' fill='none'/%3E%3Cpolygon points='1383.9,263.9 1482.4,282.6 1416.4,374.4' fill='none'/%3E%3Cpolygon points='1482.4,282.6 1480.3,354.4 1416.4,374.4' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1482.4,282.6 1600.0,268.9 1600.0,374.1 1480.3,354.4' fill='none'/%3E%3Cpolygon points='0.0,344.0 113.1,370.3 118.3,454.2 0.0,486.0' fill='none'/%3E%3Cpolygon points='113.1,370.3 188.0,371.8 118.3,454.2' fill='none'/%3E%3Cpolygon points='188.0,371.8 198.5,435.2 118.3,454.2' fill='none'/%3E%3Cpolygon points='188.0,371.8 282.9,349.5 292.9,458.6 198.5,435.2' fill='none'/%3E%3Cpolygon points='282.9,349.5 403.9,354.6 376.0,452.4 292.9,458.6' fill='none'/%3E%3Cpolygon points='403.9,354.6 452.9,358.5 466.8,442.9 376.0,452.4' fill='none'/%3E%3Cpolygon points='452.9,358.5 547.1,374.2 568.1,474.3' fill='none'/%3E%3Cpolygon points='452.9,358.5 568.1,474.3 466.8,442.9' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='547.1,374.2 664.3,359.3 654.3,476.5 568.1,474.3' fill='none'/%3E%3Cpolygon points='664.3,359.3 765.0,342.7 740.7,453.5' fill='none'/%3E%3Cpolygon points='664.3,359.3 740.7,453.5 654.3,476.5' fill='none'/%3E%3Cpolygon points='765.0,342.7 822.4,394.5 856.4,442.5 740.7,453.5' fill='none'/%3E%3Cpolygon points='822.4,394.5 915.0,368.7 856.4,442.5' fill='none'/%3E%3Cpolygon points='915.0,368.7 962.7,475.9 856.4,442.5' fill='none'/%3E%3Cpolygon points='915.0,368.7 1030.4,358.6 1024.7,457.0 962.7,475.9' fill='none'/%3E%3Cpolygon points='1030.4,358.6 1150.6,379.6 1132.2,441.4 1024.7,457.0' fill='none'/%3E%3Cpolygon points='1150.6,379.6 1208.7,362.3 1236.3,459.1 1132.2,441.4' fill='none'/%3E%3Cpolygon points='1208.7,362.3 1345.7,347.6 1346.5,441.2 1236.3,459.1' fill='none'/%3E%3Cpolygon points='1345.7,347.6 1416.4,374.4 1346.5,441.2' fill='none'/%3E%3Cpolygon points='1416.4,374.4 1433.8,440.8 1346.5,441.2' fill='none'/%3E%3Cpolygon points='1416.4,374.4 1480.3,354.4 1520.8,465.4' fill='none'/%3E%3Cpolygon points='1416.4,374.4 1520.8,465.4 1433.8,440.8' fill='none'/%3E%3Cpolygon points='1480.3,354.4 1600.0,374.1 1600.0,488.9 1520.8,465.4' fill='none'/%3E%3Cpolygon points='0.0,486.0 118.3,454.2 68.3,539.9 0.0,581.1' fill='none'/%3E%3Cpolygon points='118.3,454.2 198.5,435.2 185.8,554.4 68.3,539.9' fill='none'/%3E%3Cpolygon points='198.5,435.2 292.9,458.6 185.8,554.4' fill='none'/%3E%3Cpolygon points='292.9,458.6 257.8,535.1 185.8,554.4' fill='none'/%3E%3Cpolygon points='292.9,458.6 376.0,452.4 383.9,581.5' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='292.9,458.6 383.9,581.5 257.8,535.1' fill='none'/%3E%3Cpolygon points='376.0,452.4 466.8,442.9 383.9,581.5' fill='none'/%3E%3Cpolygon points='466.8,442.9 493.7,548.1 383.9,581.5' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='466.8,442.9 568.1,474.3 555.8,577.4 493.7,548.1' fill='none'/%3E%3Cpolygon points='568.1,474.3 654.3,476.5 555.8,577.4' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='654.3,476.5 655.8,581.0 555.8,577.4' fill='none'/%3E%3Cpolygon points='654.3,476.5 740.7,453.5 778.0,533.8 655.8,581.0' fill='none'/%3E%3Cpolygon points='740.7,453.5 856.4,442.5 820.0,534.9' fill='none'/%3E%3Cpolygon points='740.7,453.5 820.0,534.9 778.0,533.8' fill='none'/%3E%3Cpolygon points='856.4,442.5 962.7,475.9 928.9,548.1 820.0,534.9' fill='none'/%3E%3Cpolygon points='962.7,475.9 1024.7,457.0 1051.5,558.3 928.9,548.1' fill='none'/%3E%3Cpolygon points='1024.7,457.0 1132.2,441.4 1126.4,575.9 1051.5,558.3' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='1132.2,441.4 1236.3,459.1 1213.6,571.2 1126.4,575.9' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='1236.3,459.1 1346.5,441.2 1333.7,566.1 1213.6,571.2' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='1346.5,441.2 1433.8,440.8 1384.4,570.5' fill='none'/%3E%3Cpolygon points='1346.5,441.2 1384.4,570.5 1333.7,566.1' fill='none'/%3E%3Cpolygon points='1433.8,440.8 1520.8,465.4 1505.3,549.6 1384.4,570.5' fill='none'/%3E%3Cpolygon points='1520.8,465.4 1600.0,488.9 1505.3,549.6' fill='none'/%3E%3Cpolygon points='1600.0,488.9 1600.0,546.9 1505.3,549.6' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='0.0,581.1 68.3,539.9 76.3,643.0 0.0,639.1' fill='none'/%3E%3Cpolygon points='68.3,539.9 185.8,554.4 172.0,657.8 76.3,643.0' fill='none'/%3E%3Cpolygon points='185.8,554.4 257.8,535.1 267.0,658.5 172.0,657.8' fill='none'/%3E%3Cpolygon points='257.8,535.1 383.9,581.5 267.0,658.5' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='383.9,581.5 397.8,675.2 267.0,658.5' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='383.9,581.5 493.7,548.1 469.5,629.4 397.8,675.2' fill='none'/%3E%3Cpolygon points='493.7,548.1 555.8,577.4 469.5,629.4' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='555.8,577.4 577.8,621.2 469.5,629.4' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='555.8,577.4 655.8,581.0 577.8,621.2' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='655.8,581.0 686.5,665.2 577.8,621.2' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='655.8,581.0 778.0,533.8 725.7,671.2 686.5,665.2' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='778.0,533.8 820.0,534.9 871.8,662.1 725.7,671.2' fill='none'/%3E%3Cpolygon points='820.0,534.9 928.9,548.1 962.7,637.1 871.8,662.1' fill='none'/%3E%3Cpolygon points='928.9,548.1 1051.5,558.3 1030.1,669.3 962.7,637.1' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1051.5,558.3 1126.4,575.9 1030.1,669.3' fill='none'/%3E%3Cpolygon points='1126.4,575.9 1135.0,673.2 1030.1,669.3' fill='none'/%3E%3Cpolygon points='1126.4,575.9 1213.6,571.2 1236.0,634.0' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1126.4,575.9 1236.0,634.0 1135.0,673.2' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='1213.6,571.2 1333.7,566.1 1334.9,627.6 1236.0,634.0' fill='none'/%3E%3Cpolygon points='1333.7,566.1 1384.4,570.5 1431.0,636.6 1334.9,627.6' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1384.4,570.5 1505.3,549.6 1535.4,646.5 1431.0,636.6' fill='none'/%3E%3Cpolygon points='1505.3,549.6 1600.0,546.9 1600.0,646.6 1535.4,646.5' fill='none'/%3E%3Cpolygon points='0.0,639.1 76.3,643.0 0.0,717.3' fill='none'/%3E%3Cpolygon points='76.3,643.0 112.6,737.8 0.0,717.3' fill='none'/%3E%3Cpolygon points='76.3,643.0 172.0,657.8 112.6,737.8' fill='none'/%3E%3Cpolygon points='172.0,657.8 185.0,750.9 112.6,737.8' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='172.0,657.8 267.0,658.5 268.5,715.8 185.0,750.9' fill='none'/%3E%3Cpolygon points='267.0,658.5 397.8,675.2 390.3,725.8 268.5,715.8' fill='none'/%3E%3Cpolygon points='397.8,675.2 469.5,629.4 477.7,761.7 390.3,725.8' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='469.5,629.4 577.8,621.2 477.7,761.7' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='577.8,621.2 541.0,765.9 477.7,761.7' fill='none'/%3E%3Cpolygon points='577.8,621.2 686.5,665.2 541.0,765.9' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='686.5,665.2 644.4,724.5 541.0,765.9' fill='none'/%3E%3Cpolygon points='686.5,665.2 725.7,671.2 731.7,744.8' fill='none'/%3E%3Cpolygon points='686.5,665.2 731.7,744.8 644.4,724.5' fill='none'/%3E%3Cpolygon points='725.7,671.2 871.8,662.1 731.7,744.8' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='871.8,662.1 830.2,727.4 731.7,744.8' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='871.8,662.1 962.7,637.1 956.4,736.7 830.2,727.4' fill='none'/%3E%3Cpolygon points='962.7,637.1 1030.1,669.3 956.4,736.7' fill='none'/%3E%3Cpolygon points='1030.1,669.3 1060.9,731.7 956.4,736.7' fill='none'/%3E%3Cpolygon points='1030.1,669.3 1135.0,673.2 1139.7,767.0 1060.9,731.7' fill='none'/%3E%3Cpolygon points='1135.0,673.2 1236.0,634.0 1198.9,759.4 1139.7,767.0' fill='none'/%3E%3Cpolygon points='1236.0,634.0 1334.9,627.6 1305.6,717.6' fill='none'/%3E%3Cpolygon points='1236.0,634.0 1305.6,717.6 1198.9,759.4' fill='none'/%3E%3Cpolygon points='1334.9,627.6 1431.0,636.6 1305.6,717.6' fill='none'/%3E%3Cpolygon points='1431.0,636.6 1389.8,709.2 1305.6,717.6' fill='none'/%3E%3Cpolygon points='1431.0,636.6 1535.4,646.5 1502.2,724.2 1389.8,709.2' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='1535.4,646.5 1600.0,646.6 1502.2,724.2' fill='none'/%3E%3Cpolygon points='1600.0,646.6 1600.0,763.7 1502.2,724.2' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='0.0,717.3 112.6,737.8 0.0,856.8' fill='none'/%3E%3Cpolygon points='112.6,737.8 65.8,859.2 0.0,856.8' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='112.6,737.8 185.0,750.9 179.3,818.4 65.8,859.2' fill='none'/%3E%3Cpolygon points='185.0,750.9 268.5,715.8 264.9,827.5' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='185.0,750.9 264.9,827.5 179.3,818.4' fill='none'/%3E%3Cpolygon points='268.5,715.8 390.3,725.8 264.9,827.5' fill='none'/%3E%3Cpolygon points='390.3,725.8 357.6,840.9 264.9,827.5' fill='none'/%3E%3Cpolygon points='390.3,725.8 477.7,761.7 449.6,828.5 357.6,840.9' fill='none'/%3E%3Cpolygon points='477.7,761.7 541.0,765.9 574.2,810.2' fill='none'/%3E%3Cpolygon points='477.7,761.7 574.2,810.2 449.6,828.5' fill='none'/%3E%3Cpolygon points='541.0,765.9 644.4,724.5 574.2,810.2' fill='none'/%3E%3Cpolygon points='644.4,724.5 681.5,804.6 574.2,810.2' fill='none'/%3E%3Cpolygon points='644.4,724.5 731.7,744.8 681.5,804.6' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='731.7,744.8 745.8,804.4 681.5,804.6' fill='none'/%3E%3Cpolygon points='731.7,744.8 830.2,727.4 840.2,839.6 745.8,804.4' fill='none'/%3E%3Cpolygon points='830.2,727.4 956.4,736.7 947.7,841.2 840.2,839.6' fill='none'/%3E%3Cpolygon points='956.4,736.7 1060.9,731.7 1015.2,857.9' fill='none'/%3E%3Cpolygon points='956.4,736.7 1015.2,857.9 947.7,841.2' fill='none'/%3E%3Cpolygon points='1060.9,731.7 1139.7,767.0 1118.8,802.9 1015.2,857.9' fill='none'/%3E%3Cpolygon points='1139.7,767.0 1198.9,759.4 1230.0,843.7 1118.8,802.9' fill='none'/%3E%3Cpolygon points='1198.9,759.4 1305.6,717.6 1297.0,848.5 1230.0,843.7' fill='none'/%3E%3Cpolygon points='1305.6,717.6 1389.8,709.2 1430.1,810.1 1297.0,848.5' fill='none'/%3E%3Cpolygon points='1389.8,709.2 1502.2,724.2 1534.4,834.9' fill='none'/%3E%3Cpolygon points='1389.8,709.2 1534.4,834.9 1430.1,810.1' fill='none'/%3E%3Cpolygon points='1502.2,724.2 1600.0,763.7 1600.0,816.2 1534.4,834.9' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='0.0,856.8 65.8,859.2 98.7,897.9' fill='none'/%3E%3Cpolygon points='0.0,856.8 98.7,897.9 0.0,930.4' fill='none'/%3E%3Cpolygon points='65.8,859.2 179.3,818.4 188.5,894.9 98.7,897.9' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='179.3,818.4 264.9,827.5 188.5,894.9' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='264.9,827.5 273.2,914.3 188.5,894.9' fill='none'/%3E%3Cpolygon points='264.9,827.5 357.6,840.9 348.8,894.6 273.2,914.3' fill='none'/%3E%3Cpolygon points='357.6,840.9 449.6,828.5 479.8,951.2 348.8,894.6' fill='none'/%3E%3Cpolygon points='449.6,828.5 574.2,810.2 479.8,951.2' fill='none'/%3E%3Cpolygon points='574.2,810.2 554.6,924.2 479.8,951.2' fill='none'/%3E%3Cpolygon points='574.2,810.2 681.5,804.6 679.2,926.8' fill='none'/%3E%3Cpolygon points='574.2,810.2 679.2,926.8 554.6,924.2' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='681.5,804.6 745.8,804.4 679.2,926.8' fill='none'/%3E%3Cpolygon points='745.8,804.4 759.6,900.0 679.2,926.8' fill='none'/%3E%3Cpolygon points='745.8,804.4 840.2,839.6 818.8,926.4 759.6,900.0' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='840.2,839.6 947.7,841.2 919.9,948.5' fill='none'/%3E%3Cpolygon points='840.2,839.6 919.9,948.5 818.8,926.4' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='947.7,841.2 1015.2,857.9 919.9,948.5' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1015.2,857.9 1053.4,952.4 919.9,948.5' fill='none'/%3E%3Cpolygon points='1015.2,857.9 1118.8,802.9 1053.4,952.4' fill='none'/%3E%3Cpolygon points='1118.8,802.9 1100.6,902.2 1053.4,952.4' fill='none'/%3E%3Cpolygon points='1118.8,802.9 1230.0,843.7 1230.5,939.1 1100.6,902.2' fill='none'/%3E%3Cpolygon points='1230.0,843.7 1297.0,848.5 1288.9,945.5' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='1230.0,843.7 1288.9,945.5 1230.5,939.1' fill='none'/%3E%3Cpolygon points='1297.0,848.5 1430.1,810.1 1288.9,945.5' fill='none'/%3E%3Cpolygon points='1430.1,810.1 1384.1,935.1 1288.9,945.5' fill='none'/%3E%3Cpolygon points='1430.1,810.1 1534.4,834.9 1487.5,935.5' fill='none'/%3E%3Cpolygon points='1430.1,810.1 1487.5,935.5 1384.1,935.1' fill='none'/%3E%3Cpolygon points='1534.4,834.9 1600.0,816.2 1600.0,906.3 1487.5,935.5' fill='none'/%3E%3Cpolygon points='0.0,930.4 98.7,897.9 94.1,999.7 0.0,1014.5' fill='none'/%3E%3Cpolygon points='98.7,897.9 188.5,894.9 160.7,1019.8' fill='none'/%3E%3Cpolygon points='98.7,897.9 160.7,1019.8 94.1,999.7' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='188.5,894.9 273.2,914.3 160.7,1019.8' fill='none'/%3E%3Cpolygon points='273.2,914.3 261.5,1011.2 160.7,1019.8' fill='none'/%3E%3Cpolygon points='273.2,914.3 348.8,894.6 261.5,1011.2' fill='none'/%3E%3Cpolygon points='348.8,894.6 392.3,999.9 261.5,1011.2' fill='none'/%3E%3Cpolygon points='348.8,894.6 479.8,951.2 470.1,989.1 392.3,999.9' fill='none'/%3E%3Cpolygon points='479.8,951.2 554.6,924.2 588.8,1017.7 470.1,989.1' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='554.6,924.2 679.2,926.8 588.8,1017.7' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='679.2,926.8 653.7,998.0 588.8,1017.7' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='679.2,926.8 759.6,900.0 653.7,998.0' fill='none'/%3E%3Cpolygon points='759.6,900.0 764.9,998.4 653.7,998.0' fill='none'/%3E%3Cpolygon points='759.6,900.0 818.8,926.4 861.4,1005.9 764.9,998.4' fill='none'/%3E%3Cpolygon points='818.8,926.4 919.9,948.5 920.0,1014.1 861.4,1005.9' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='919.9,948.5 1053.4,952.4 1028.7,1001.1 920.0,1014.1' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='1053.4,952.4 1100.6,902.2 1157.4,990.4' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='1053.4,952.4 1157.4,990.4 1028.7,1001.1' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='1100.6,902.2 1230.5,939.1 1233.1,1004.1 1157.4,990.4' fill='none'/%3E%3Cpolygon points='1230.5,939.1 1288.9,945.5 1321.3,1043.1 1233.1,1004.1' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1288.9,945.5 1384.1,935.1 1424.6,992.5 1321.3,1043.1' fill='none'/%3E%3Cpolygon points='1384.1,935.1 1487.5,935.5 1424.6,992.5' fill='none'/%3E%3Cpolygon points='1487.5,935.5 1529.7,1010.4 1424.6,992.5' fill='none'/%3E%3Cpolygon points='1487.5,935.5 1600.0,906.3 1600.0,1004.9 1529.7,1010.4' fill='none'/%3E%3Cpolygon points='0.0,1014.5 94.1,999.7 83.1,1127.7' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='0.0,1014.5 83.1,1127.7 0.0,1123.4' fill='none'/%3E%3Cpolygon points='94.1,999.7 160.7,1019.8 83.1,1127.7' fill='none'/%3E%3Cpolygon points='160.7,1019.8 182.7,1125.6 83.1,1127.7' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='160.7,1019.8 261.5,1011.2 304.2,1130.4' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='160.7,1019.8 304.2,1130.4 182.7,1125.6' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='261.5,1011.2 392.3,999.9 351.1,1114.7 304.2,1130.4' fill='none'/%3E%3Cpolygon points='392.3,999.9 470.1,989.1 351.1,1114.7' fill='none'/%3E%3Cpolygon points='470.1,989.1 452.5,1088.3 351.1,1114.7' fill='none'/%3E%3Cpolygon points='470.1,989.1 588.8,1017.7 546.0,1135.9 452.5,1088.3' fill='none'/%3E%3Cpolygon points='588.8,1017.7 653.7,998.0 684.7,1088.3 546.0,1135.9' fill='none'/%3E%3Cpolygon points='653.7,998.0 764.9,998.4 684.7,1088.3' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='764.9,998.4 762.3,1107.9 684.7,1088.3' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='764.9,998.4 861.4,1005.9 870.9,1093.7 762.3,1107.9' fill='none'/%3E%3Cpolygon points='861.4,1005.9 920.0,1014.1 920.0,1135.3 870.9,1093.7' fill='none'/%3E%3Cpolygon points='920.0,1014.1 1028.7,1001.1 1038.4,1133.2 920.0,1135.3' fill='none'/%3E%3Cpolygon points='1028.7,1001.1 1157.4,990.4 1145.9,1094.5 1038.4,1133.2' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='1157.4,990.4 1233.1,1004.1 1228.3,1087.0 1145.9,1094.5' fill='none'/%3E%3Cpolygon points='1233.1,1004.1 1321.3,1043.1 1228.3,1087.0' fill='none'/%3E%3Cpolygon points='1321.3,1043.1 1335.7,1125.4 1228.3,1087.0' fill='none'/%3E%3Cpolygon points='1321.3,1043.1 1424.6,992.5 1426.0,1098.9' fill='none'/%3E%3Cpolygon points='1321.3,1043.1 1426.0,1098.9 1335.7,1125.4' fill='none'/%3E%3Cpolygon points='1424.6,992.5 1529.7,1010.4 1488.9,1087.4 1426.0,1098.9' fill='none'/%3E%3Cpolygon points='1529.7,1010.4 1600.0,1004.9 1600.0,1117.7' fill='none'/%3E%3Cpolygon points='1529.7,1010.4 1600.0,1117.7 1488.9,1087.4' fill='none'/%3E%3Cpolygon points='0.0,1123.4 83.1,1127.7 112.3,1200.0' fill='none'/%3E%3Cpolygon points='0.0,1123.4 112.3,1200.0 0.0,1200.0' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='83.1,1127.7 182.7,1125.6 112.3,1200.0' fill='none'/%3E%3Cpolygon points='182.7,1125.6 170.3,1200.0 112.3,1200.0' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='182.7,1125.6 304.2,1130.4 304.6,1200.0 170.3,1200.0' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='304.2,1130.4 351.1,1114.7 387.3,1200.0 304.6,1200.0' fill='none'/%3E%3Cpolygon points='351.1,1114.7 452.5,1088.3 463.3,1200.0 387.3,1200.0' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='452.5,1088.3 546.0,1135.9 572.0,1200.0' fill='none'/%3E%3Cpolygon points='452.5,1088.3 572.0,1200.0 463.3,1200.0' fill='none'/%3E%3Cpolygon points='546.0,1135.9 684.7,1088.3 572.0,1200.0' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='684.7,1088.3 645.3,1200.0 572.0,1200.0' fill='none'/%3E%3Cpolygon points='684.7,1088.3 762.3,1107.9 761.8,1200.0 645.3,1200.0' fill='none'/%3E%3Cpolygon points='762.3,1107.9 870.9,1093.7 867.0,1200.0 761.8,1200.0' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='870.9,1093.7 920.0,1135.3 925.1,1200.0 867.0,1200.0' fill='none'/%3E%3Cpolygon points='920.0,1135.3 1038.4,1133.2 1063.0,1200.0 925.1,1200.0' fill='none'/%3E%3Cpolygon points='1038.4,1133.2 1145.9,1094.5 1063.0,1200.0' fill='none'/%3E%3Cpolygon points='1145.9,1094.5 1103.4,1200.0 1063.0,1200.0' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1145.9,1094.5 1228.3,1087.0 1235.6,1200.0 1103.4,1200.0' fill='none'/%3E%3Cpolygon points='1228.3,1087.0 1335.7,1125.4 1340.3,1200.0 1235.6,1200.0' fill='none'/%3E%3Cpolygon points='1335.7,1125.4 1426.0,1098.9 1428.0,1200.0 1340.3,1200.0' fill='none'/%3E%3Cpolygon points='1426.0,1098.9 1488.9,1087.4 1531.3,1200.0 1428.0,1200.0' fill='none'/%3E%3Cpolygon points='1488.9,1087.4 1600.0,1117.7 1600.0,1200.0' fill='none'/%3E%3Cpolygon points='1488.9,1087.4 1600.0,1200.0 1531.3,1200.0' fill='%238a6ee4' fill-opacity='0.04'/%3E%3C/g%3E%3C/svg%3E");
|
|
157
|
+
background-size: cover;
|
|
158
|
+
background-position: center;
|
|
159
|
+
-webkit-mask-image: var(--vitral-lattice-mask);
|
|
160
|
+
mask-image: var(--vitral-lattice-mask);
|
|
161
|
+
/* The near layer of the parallax, so the lead sits in front of the light. */
|
|
162
|
+
transform: translate3d(calc(var(--vitral-shift-x, 0) * -10px),
|
|
163
|
+
calc(var(--vitral-shift-y, 0) * -10px + var(--vitral-drift-near, 0px)), 0);
|
|
164
|
+
transition: transform .2s cubic-bezier(.2, .7, .3, 1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* A narrow screen is all reading column, so the leading thins out everywhere
|
|
168
|
+
rather than changing shape at a breakpoint. */
|
|
169
|
+
@media (max-width: 64rem) {
|
|
170
|
+
:root { --vitral-lattice-mask: linear-gradient(90deg, rgba(0, 0, 0, 0.5) 0 100%); }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* The live lattice, drawn by the optional controller. Same place, same mask,
|
|
174
|
+
same job as the static one above, except that its nodes lean toward the
|
|
175
|
+
pointer. The static one stands down when this arrives. */
|
|
176
|
+
.vitral-lattice {
|
|
177
|
+
position: fixed;
|
|
178
|
+
left: 0;
|
|
179
|
+
right: 0;
|
|
180
|
+
top: 0;
|
|
181
|
+
z-index: -1;
|
|
182
|
+
pointer-events: none;
|
|
183
|
+
opacity: 0.8;
|
|
184
|
+
-webkit-mask-image: var(--vitral-lattice-mask);
|
|
185
|
+
mask-image: var(--vitral-lattice-mask);
|
|
186
|
+
transform: translate3d(calc(var(--vitral-shift-x, 0) * -10px),
|
|
187
|
+
calc(var(--vitral-shift-y, 0) * -10px + var(--vitral-drift-near, 0px)), 0);
|
|
188
|
+
transition: transform .2s cubic-bezier(.2, .7, .3, 1);
|
|
189
|
+
}
|
|
190
|
+
.vitral-live::after { display: none; }
|
|
191
|
+
|
|
192
|
+
/* One recipe for everything that is meant to read as a pane of glass.
|
|
193
|
+
.vitral-pane is public: put it on your own markup and the page around a
|
|
194
|
+
dashboard is made of the same window. */
|
|
195
|
+
.vitral-pane, .janela-frame > *, .janela-page > .janela-pane, .janela-card, .janela-flash, .janela-list-row {
|
|
196
|
+
position: relative;
|
|
197
|
+
padding: calc(var(--janela-space) * 4) calc(var(--janela-space) * 5);
|
|
198
|
+
background: linear-gradient(var(--vitral-glass), var(--vitral-glass)), var(--vitral-pane-1);
|
|
199
|
+
-webkit-backdrop-filter: var(--vitral-blur);
|
|
200
|
+
backdrop-filter: var(--vitral-blur);
|
|
201
|
+
border: 1px solid var(--vitral-came);
|
|
202
|
+
border-radius: var(--vitral-radius);
|
|
203
|
+
box-shadow: var(--vitral-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.75);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* A window worth looking at cycles its colours rather than repeating one, so a
|
|
207
|
+
pane takes its hue from where it sits. .vitral-panes on a container does the
|
|
208
|
+
same for your own markup. */
|
|
209
|
+
.vitral-panes > *:nth-child(5n + 2),
|
|
210
|
+
.janela-frame > *:nth-child(5n + 2),
|
|
211
|
+
.janela-page > .janela-pane:nth-child(5n + 2),
|
|
212
|
+
.janela-list-row:nth-child(5n + 2), .janela-card:nth-child(5n + 2) {
|
|
213
|
+
background: linear-gradient(var(--vitral-glass), var(--vitral-glass)), var(--vitral-pane-2);
|
|
214
|
+
}
|
|
215
|
+
.vitral-panes > *:nth-child(5n + 3),
|
|
216
|
+
.janela-frame > *:nth-child(5n + 3),
|
|
217
|
+
.janela-page > .janela-pane:nth-child(5n + 3),
|
|
218
|
+
.janela-list-row:nth-child(5n + 3), .janela-card:nth-child(5n + 3) {
|
|
219
|
+
background: linear-gradient(var(--vitral-glass), var(--vitral-glass)), var(--vitral-pane-3);
|
|
220
|
+
}
|
|
221
|
+
.vitral-panes > *:nth-child(5n + 4),
|
|
222
|
+
.janela-frame > *:nth-child(5n + 4),
|
|
223
|
+
.janela-page > .janela-pane:nth-child(5n + 4),
|
|
224
|
+
.janela-list-row:nth-child(5n + 4), .janela-card:nth-child(5n + 4) {
|
|
225
|
+
background: linear-gradient(var(--vitral-glass), var(--vitral-glass)), var(--vitral-pane-4);
|
|
226
|
+
}
|
|
227
|
+
.vitral-panes > *:nth-child(5n + 5),
|
|
228
|
+
.janela-frame > *:nth-child(5n + 5),
|
|
229
|
+
.janela-page > .janela-pane:nth-child(5n + 5),
|
|
230
|
+
.janela-list-row:nth-child(5n + 5), .janela-card:nth-child(5n + 5) {
|
|
231
|
+
background: linear-gradient(var(--vitral-glass), var(--vitral-glass)), var(--vitral-pane-5);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* The line of light along the top edge, where real glass catches it. A canvas
|
|
235
|
+
is a replaced element and takes no pseudo-element, so charts go without. */
|
|
236
|
+
.vitral-pane::before, .janela-frame > *::before,
|
|
237
|
+
.janela-card::before, .janela-list-row::before {
|
|
238
|
+
content: "";
|
|
239
|
+
position: absolute;
|
|
240
|
+
inset: 0 0 auto;
|
|
241
|
+
height: 1px;
|
|
242
|
+
border-radius: var(--vitral-radius) var(--vitral-radius) 0 0;
|
|
243
|
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.9), transparent);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* Inside a panel the pane is content: the grid item carries the glass, since
|
|
247
|
+
a table's caption renders outside the table's own border box and would
|
|
248
|
+
otherwise float above the colour. */
|
|
249
|
+
.janela-frame .janela-pane { background: none; border: 0; box-shadow: none; padding: 0; }
|
|
250
|
+
|
|
251
|
+
.vitral-pane::after, .janela-frame > *::after,
|
|
252
|
+
.janela-card::after, .janela-list-row::after {
|
|
253
|
+
content: "";
|
|
254
|
+
position: absolute;
|
|
255
|
+
inset: 0;
|
|
256
|
+
z-index: -1;
|
|
257
|
+
border-radius: inherit;
|
|
258
|
+
pointer-events: none;
|
|
259
|
+
background: linear-gradient(116deg,
|
|
260
|
+
rgba(255, 255, 255, 0.5) 0 10%,
|
|
261
|
+
rgba(255, 255, 255, 0.16) 24%,
|
|
262
|
+
rgba(255, 255, 255, 0) 44%);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* A collapsed table drops its corner radius, and glass without corners is a
|
|
266
|
+
pane of a different shape. */
|
|
267
|
+
table.janela-pane { border-collapse: separate; border-spacing: 0; }
|
|
268
|
+
table.janela-pane caption { padding-bottom: calc(var(--janela-space) * 2); }
|
|
269
|
+
table.janela-pane td:first-child { padding-left: 0; }
|
|
270
|
+
|
|
271
|
+
.janela-value-number { letter-spacing: -0.02em; }
|
|
272
|
+
.janela-value-label { opacity: 0.75; }
|
|
273
|
+
|
|
274
|
+
table.janela-pane button[aria-pressed="true"] {
|
|
275
|
+
background: var(--janela-accent);
|
|
276
|
+
color: white;
|
|
277
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.janela-empty { color: var(--vitral-muted); }
|
|
281
|
+
.janela-error { border-color: rgba(190, 60, 60, 0.5); }
|
|
282
|
+
|
|
283
|
+
/* Janela's own pages, which carry the theme themselves because the engine owns
|
|
284
|
+
that layout (ADR 011). */
|
|
285
|
+
.janela-page { padding-block: calc(var(--janela-space) * 10); }
|
|
286
|
+
.janela-heading { letter-spacing: -0.02em; }
|
|
287
|
+
.janela-list { display: grid; gap: calc(var(--janela-space) * 2); }
|
|
288
|
+
.janela-list-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
|
|
289
|
+
.janela-field input, .janela-field select {
|
|
290
|
+
background: var(--vitral-glass-strong);
|
|
291
|
+
border-color: var(--vitral-came-soft);
|
|
292
|
+
border-radius: 8px;
|
|
293
|
+
}
|
|
294
|
+
.janela-actions { display: flex; gap: calc(var(--janela-space) * 3); align-items: center; flex-wrap: wrap; }
|
|
295
|
+
|
|
296
|
+
/* A control made of the same glass. */
|
|
297
|
+
.vitral-button {
|
|
298
|
+
display: inline-block;
|
|
299
|
+
padding: .62rem 1.25rem;
|
|
300
|
+
font: inherit;
|
|
301
|
+
font-weight: 550;
|
|
302
|
+
color: var(--vitral-ink);
|
|
303
|
+
text-decoration: none;
|
|
304
|
+
cursor: pointer;
|
|
305
|
+
background: var(--vitral-glass-strong);
|
|
306
|
+
-webkit-backdrop-filter: var(--vitral-blur);
|
|
307
|
+
backdrop-filter: var(--vitral-blur);
|
|
308
|
+
border: 1px solid var(--vitral-came);
|
|
309
|
+
border-radius: 10px;
|
|
310
|
+
box-shadow: var(--vitral-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.75);
|
|
311
|
+
}
|
|
312
|
+
.vitral-button:hover { border-color: rgba(26, 38, 54, 0.62); }
|
|
313
|
+
.vitral-button-primary {
|
|
314
|
+
background: linear-gradient(180deg, rgb(86, 180, 242), rgb(25, 112, 176));
|
|
315
|
+
color: white;
|
|
316
|
+
box-shadow: 0 10px 24px -12px rgba(25, 112, 176, 0.9), inset 0 1px 0 rgba(255, 255, 255, 0.45);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* Glass is an effect, not a requirement. Anyone who asked for less of it, or
|
|
320
|
+
whose browser cannot do it, gets the same window in plain panels. */
|
|
321
|
+
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
|
322
|
+
.vitral-pane, .janela-pane, .janela-card, .janela-flash, .janela-list-row, .vitral-button {
|
|
323
|
+
background: rgba(255, 255, 255, 0.94);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
|
|
328
|
+
.vitral-pane, .janela-pane, .janela-card, .janela-flash, .janela-list-row, .vitral-button {
|
|
329
|
+
background: #ffffff;
|
|
330
|
+
-webkit-backdrop-filter: none;
|
|
331
|
+
backdrop-filter: none;
|
|
332
|
+
border-color: var(--vitral-came-soft);
|
|
333
|
+
}
|
|
334
|
+
.vitral::before, .vitral::after, .janela-page::before, .janela-page::after { display: none; }
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
@media (prefers-reduced-motion: reduce) {
|
|
338
|
+
.vitral::before, .vitral::after, .vitral-lattice,
|
|
339
|
+
.janela-page::before, .janela-page::after {
|
|
340
|
+
transform: none;
|
|
341
|
+
transition: none;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
module Janela
|
|
2
2
|
class ApplicationController < Janela.parent_controller.constantize
|
|
3
|
+
# A host's own route helpers, so host code inherited from its
|
|
4
|
+
# ApplicationController works in here without knowing it is in an engine
|
|
5
|
+
# (ADR 022). Included before the engine's own helpers are used, and it
|
|
6
|
+
# defines none of their names, so nothing of Janela's is shadowed.
|
|
7
|
+
include Janela::HostRoutes
|
|
8
|
+
helper Janela::HostRoutes
|
|
9
|
+
|
|
3
10
|
# A frame request needs no layout, since Turbo keeps only the matching
|
|
4
11
|
# frame. A direct request gets Janela's own minimal layout, because a host
|
|
5
12
|
# layout's route helpers cannot resolve inside an isolated engine (ADR 011).
|
|
6
13
|
layout -> { turbo_frame_request? ? false : "janela/application" }
|
|
7
14
|
|
|
8
15
|
rescue_from Janela::NotFound, with: :janela_not_found
|
|
16
|
+
# A frame or a pane row the host's scope cannot see is the same answer as a
|
|
17
|
+
# measure that does not exist, and inside a turbo frame it has to be said
|
|
18
|
+
# in the frame rather than by the host's error page.
|
|
19
|
+
rescue_from ActiveRecord::RecordNotFound, with: :janela_not_found
|
|
9
20
|
rescue_from Janela::BadRequest, with: :janela_bad_request
|
|
10
21
|
|
|
11
22
|
private
|
|
23
|
+
def filters
|
|
24
|
+
q = params[:q]
|
|
25
|
+
q.is_a?(ActionController::Parameters) ? q.permit!.to_h : {}
|
|
26
|
+
end
|
|
27
|
+
|
|
12
28
|
# Pundit defines policy_scope on the host's ApplicationController, which
|
|
13
29
|
# this inherits from, so authorisation applies without Janela depending
|
|
14
30
|
# on Pundit or being configured.
|
|
@@ -16,6 +32,15 @@ module Janela
|
|
|
16
32
|
respond_to?(:policy_scope, true) ? policy_scope(model) : model.all
|
|
17
33
|
end
|
|
18
34
|
|
|
35
|
+
# A host that scopes frames by owner would hide a frame created without
|
|
36
|
+
# one, so the analyst's new dashboard would vanish as it was saved.
|
|
37
|
+
# Janela cannot know what owns a frame, so the host says, the same way it
|
|
38
|
+
# says what the scope is: by defining a method. A host with no tenancy
|
|
39
|
+
# defines nothing and gets a nil owner, which is right for it (ADR 014).
|
|
40
|
+
def janela_frame_owner
|
|
41
|
+
super if defined?(super)
|
|
42
|
+
end
|
|
43
|
+
|
|
19
44
|
def janela_not_found(error)
|
|
20
45
|
janela_error(error, :not_found, "There is no such pane.")
|
|
21
46
|
end
|