houseaccount 0.1.0 → 0.3.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 +163 -1
- data/MIT-LICENSE +22 -0
- data/README.md +230 -20
- data/art/autopilot.svg +9 -0
- data/art/design.svg +8 -0
- data/art/down.html.erb +95 -0
- data/art/fountain.svg +9 -0
- data/art/goldrush.svg +7 -0
- data/art/houseaccount.svg +9 -0
- data/art/houston.svg +8 -0
- data/art/icons.txt +184 -0
- data/config/locales/houseaccount.en.yml +12 -0
- data/lib/houseaccount/down.rb +19 -0
- data/lib/houseaccount/engine.rb +53 -0
- data/lib/houseaccount/form_builder/pins.rb +30 -0
- data/lib/houseaccount/form_builder.rb +84 -0
- data/lib/houseaccount/helpers/chats/asides.rb +49 -0
- data/lib/houseaccount/helpers/chats/asks.rb +40 -0
- data/lib/houseaccount/helpers/chats.rb +42 -0
- data/lib/houseaccount/helpers/dialogs.rb +47 -0
- data/lib/houseaccount/helpers/flows.rb +45 -0
- data/lib/houseaccount/helpers/heads.rb +42 -0
- data/lib/houseaccount/helpers/logos.rb +43 -0
- data/lib/houseaccount/helpers/notices.rb +55 -0
- data/lib/houseaccount/helpers.rb +13 -0
- data/lib/houseaccount/marks.rb +14 -0
- data/lib/houseaccount/message.rb +11 -0
- data/lib/houseaccount/version.rb +2 -2
- data/lib/houseaccount.rb +35 -2
- data/public/_headers +28 -0
- data/public/css/houseaccount.css +1 -0
- data/public/down/autopilot.html +87 -0
- data/public/down/design.html +87 -0
- data/public/down/fountain.html +87 -0
- data/public/down/goldrush.html +87 -0
- data/public/down/houseaccount.html +84 -0
- data/public/down/houston.html +87 -0
- data/public/favicon/autopilot-180.png +0 -0
- data/public/favicon/autopilot-192.png +0 -0
- data/public/favicon/autopilot-512.png +0 -0
- data/public/favicon/autopilot-96.png +0 -0
- data/public/favicon/autopilot-dev.svg +9 -0
- data/public/favicon/autopilot.ico +0 -0
- data/public/favicon/autopilot.svg +9 -0
- data/public/favicon/autopilot.webmanifest +12 -0
- data/public/favicon/design-180.png +0 -0
- data/public/favicon/design-192.png +0 -0
- data/public/favicon/design-512.png +0 -0
- data/public/favicon/design-96.png +0 -0
- data/public/favicon/design-dev.svg +8 -0
- data/public/favicon/design.ico +0 -0
- data/public/favicon/design.svg +8 -0
- data/public/favicon/design.webmanifest +12 -0
- data/public/favicon/fountain-180.png +0 -0
- data/public/favicon/fountain-192.png +0 -0
- data/public/favicon/fountain-512.png +0 -0
- data/public/favicon/fountain-96.png +0 -0
- data/public/favicon/fountain-dev.svg +9 -0
- data/public/favicon/fountain.ico +0 -0
- data/public/favicon/fountain.svg +9 -0
- data/public/favicon/fountain.webmanifest +12 -0
- data/public/favicon/goldrush-180.png +0 -0
- data/public/favicon/goldrush-192.png +0 -0
- data/public/favicon/goldrush-512.png +0 -0
- data/public/favicon/goldrush-96.png +0 -0
- data/public/favicon/goldrush-dev.svg +7 -0
- data/public/favicon/goldrush.ico +0 -0
- data/public/favicon/goldrush.svg +7 -0
- data/public/favicon/goldrush.webmanifest +12 -0
- data/public/favicon/houseaccount-180.png +0 -0
- data/public/favicon/houseaccount-192.png +0 -0
- data/public/favicon/houseaccount-512.png +0 -0
- data/public/favicon/houseaccount-96.png +0 -0
- data/public/favicon/houseaccount-dev.svg +9 -0
- data/public/favicon/houseaccount.ico +0 -0
- data/public/favicon/houseaccount.svg +9 -0
- data/public/favicon/houseaccount.webmanifest +12 -0
- data/public/favicon/houston-180.png +0 -0
- data/public/favicon/houston-192.png +0 -0
- data/public/favicon/houston-512.png +0 -0
- data/public/favicon/houston-96.png +0 -0
- data/public/favicon/houston-dev.svg +8 -0
- data/public/favicon/houston.ico +0 -0
- data/public/favicon/houston.svg +8 -0
- data/public/favicon/houston.webmanifest +12 -0
- data/public/favicon.ico +0 -0
- data/public/font/bootstrap-icons-6LILWPE2.woff2 +0 -0
- data/public/font/quicksand-500-latin-QHYSRFVK.woff2 +0 -0
- data/public/font/quicksand-500-latin-ext-QZ4PXVHB.woff2 +0 -0
- data/public/font/quicksand-500-latin-ext.woff2 +0 -0
- data/public/font/quicksand-500-latin.woff2 +0 -0
- data/public/font/quicksand-500-vietnamese-RRA5CUQB.woff2 +0 -0
- data/public/font/quicksand-500-vietnamese.woff2 +0 -0
- data/public/index.html +298 -0
- data/public/js/houseaccount.js +144 -0
- data/public/logo/autopilot.png +0 -0
- data/public/logo/autopilot.svg +9 -0
- data/public/logo/design.png +0 -0
- data/public/logo/design.svg +8 -0
- data/public/logo/fountain.png +0 -0
- data/public/logo/fountain.svg +9 -0
- data/public/logo/goldrush.png +0 -0
- data/public/logo/goldrush.svg +7 -0
- data/public/logo/houseaccount.png +0 -0
- data/public/logo/houseaccount.svg +9 -0
- data/public/logo/houston.png +0 -0
- data/public/logo/houston.svg +8 -0
- data/public/page/400.html +118 -0
- data/public/page/404.html +118 -0
- data/public/page/406-unsupported-browser.html +118 -0
- data/public/page/422.html +118 -0
- data/public/page/500.html +118 -0
- data/public/site.webmanifest +12 -0
- data/public/theme/bootstrap.css +11 -0
- data/public/theme/dawn.css +189 -0
- data/public/theme/dracula.css +189 -0
- data/public/theme/gruvbox.css +189 -0
- data/public/theme/monokai.css +190 -0
- data/public/theme/nord.css +189 -0
- data/public/theme/one_dark.css +189 -0
- data/public/theme/solarized.css +188 -0
- data/public/theme/tokyo_night.css +189 -0
- data/public/v0.3.0/css/houseaccount.css +1 -0
- data/public/v0.3.0/font/bootstrap-icons-6LILWPE2.woff2 +0 -0
- data/public/v0.3.0/font/quicksand-500-latin-QHYSRFVK.woff2 +0 -0
- data/public/v0.3.0/font/quicksand-500-latin-ext-QZ4PXVHB.woff2 +0 -0
- data/public/v0.3.0/font/quicksand-500-latin-ext.woff2 +0 -0
- data/public/v0.3.0/font/quicksand-500-latin.woff2 +0 -0
- data/public/v0.3.0/font/quicksand-500-vietnamese-RRA5CUQB.woff2 +0 -0
- data/public/v0.3.0/font/quicksand-500-vietnamese.woff2 +0 -0
- data/public/v0.3.0/js/houseaccount.js +144 -0
- data/public/v0.3.0/theme/bootstrap.css +11 -0
- data/public/v0.3.0/theme/dawn.css +189 -0
- data/public/v0.3.0/theme/dracula.css +189 -0
- data/public/v0.3.0/theme/gruvbox.css +189 -0
- data/public/v0.3.0/theme/monokai.css +190 -0
- data/public/v0.3.0/theme/nord.css +189 -0
- data/public/v0.3.0/theme/one_dark.css +189 -0
- data/public/v0.3.0/theme/solarized.css +188 -0
- data/public/v0.3.0/theme/tokyo_night.css +189 -0
- metadata +190 -12
- data/LICENSE.txt +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 939b9379b9f9074c36dfafeaa68f37c91154a36976cd64025170653a71b1918a
|
|
4
|
+
data.tar.gz: 552f0b7b147cb46cb7edab29c293a6d570cd936f6198028694364f8de9f895c0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0da5799f9fcfbb25157745e1641dbbace60b62d40eb344774ea123e2e909f08d912384810b1d9500a5c17d16ad7b17323f5a8fdd7e04ed5907d861a4e9adfb82
|
|
7
|
+
data.tar.gz: 06e33a1b81ca80e1ccc595be50bdfcc64b4756cda9e1de9741251e91183e5d742a62ed552c22d4017e1149f40de5196947d63afef0d1ee3026e761315f34396e
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,168 @@ For more information about changelogs, check [Keep a Changelog](http://keepachan
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## 0.3.0 - 2026-09-14
|
|
11
|
+
|
|
12
|
+
* [Fix] A chat scrolls inside its card, and the page stands still
|
|
13
|
+
|
|
14
|
+
The flow holding a thread is a flex item of `main`, and `flex: 1` sized it to its content
|
|
15
|
+
over the height it was given, so a long thread grew the page and the way out scrolled off.
|
|
16
|
+
The column is `flex: none` at the window's height now, and only the thread scrolls.
|
|
17
|
+
|
|
18
|
+
* [Fix] Sending a question keeps the reader where they were
|
|
19
|
+
|
|
20
|
+
The ask form under a chat asks Turbo to `replace`, so the redirect back to the page it is
|
|
21
|
+
on is a refresh — morphed in place, scroll kept — rather than a visit landing at the top.
|
|
22
|
+
|
|
23
|
+
* [Fix] A Turbo refresh morphs the page and keeps the scroll
|
|
24
|
+
|
|
25
|
+
The head tags now say `turbo-refresh-method: morph` and `turbo-refresh-scroll: preserve`,
|
|
26
|
+
so an answer landing in a thread leaves the reader at the bottom where they typed, rather
|
|
27
|
+
than at the top of a page drawn again. The layout also names `HouseAccount.app` by its
|
|
28
|
+
current spelling where a page has no title of its own.
|
|
29
|
+
|
|
30
|
+
* [Feature] `flow` draws the page of a signup flow around a card
|
|
31
|
+
|
|
32
|
+
The mark over the wall, a title, a line, one card and the way out, from a host layout's
|
|
33
|
+
one call. The view says its words in `content_for` — `:title`, `:subtitle` and `:width`,
|
|
34
|
+
the card's, which is how a chat's page gets a wider card than a form's — and the layout
|
|
35
|
+
passes `footer:`, pieces joined by a middot, and `logo:`. The column and the card are the
|
|
36
|
+
gem's rules again, `.flow` and `.flow-card`.
|
|
37
|
+
|
|
38
|
+
* [Fix] No gutter is kept for a scrollbar a page has not got
|
|
39
|
+
|
|
40
|
+
Bootstrap 6 reserves the column on every page; a browser with classic scrollbars painted
|
|
41
|
+
its empty track beside a page that fit. The gutter now appears with the scrollbar alone.
|
|
42
|
+
|
|
43
|
+
* [Fix] The versioned copy follows the build until the version is tagged
|
|
44
|
+
|
|
45
|
+
The head helper links `/v<version>/`, and that folder was written once and left, so a
|
|
46
|
+
host reading the gem from a path kept the stylesheet of the day the folder was made.
|
|
47
|
+
Every build now rewrites it until `v<version>` is tagged, and never after.
|
|
48
|
+
|
|
49
|
+
* [Feature] `HouseAccount.one_time_code` is the text that carries a code
|
|
50
|
+
|
|
51
|
+
Every app texting a 6-digit code sends the same two lines, the sentence and the
|
|
52
|
+
`@host #code` Safari reads a code from, and reads them off the gem's locale file rather
|
|
53
|
+
than keeping a copy of its own.
|
|
54
|
+
|
|
55
|
+
* [Breaking change] The constant is `HouseAccount`
|
|
56
|
+
|
|
57
|
+
`HouseAccount::Message`, `HouseAccount::Engine`, `HouseAccount.app`: the house's name
|
|
58
|
+
as the house writes it. The gem and the npm package stay `houseaccount`, since a gem's
|
|
59
|
+
name fixes only the file Bundler requires.
|
|
60
|
+
|
|
61
|
+
* [Feature] A host's layout nests on the gem's
|
|
62
|
+
|
|
63
|
+
A layout hands its page over as `content_for :content` and renders the gem's as a
|
|
64
|
+
template; the bare layout's body now fills the window. The wall stays the gem's, from
|
|
65
|
+
`data-controller='wall'`; what a host draws in front of it, a column and a card say, is
|
|
66
|
+
the host's to style.
|
|
67
|
+
|
|
68
|
+
* [Fix] Body text is Bootstrap's own, and a chat bubble has its sheen back
|
|
69
|
+
|
|
70
|
+
The stylesheet set every page to Helvetica at 14px, a choice made for tables that came
|
|
71
|
+
over with their rules; a page a customer reads on a phone wants Bootstrap's system face at
|
|
72
|
+
16px, and a host wanting another sets the two properties itself. A bubble wears
|
|
73
|
+
`--bs-gradient` again, as a message thread's do.
|
|
74
|
+
|
|
75
|
+
* [Feature] `dialog` draws a link and the dialog it opens
|
|
76
|
+
|
|
77
|
+
Bootstrap's `<dialog>` with a heading, an X, the block and an `Okay`, and the link that
|
|
78
|
+
opens it through the plugin's own `data-bs-toggle`, sharing an id made from the link's
|
|
79
|
+
words — so no page writes the markup or invents a name, and the `dialog` controller
|
|
80
|
+
carried here from fountain goes.
|
|
81
|
+
|
|
82
|
+
* [Fix] A phone field refuses an eleventh digit rather than taking it and cutting it back
|
|
83
|
+
|
|
84
|
+
A submit shut until the field is valid saw the eleven digits before the format cut them
|
|
85
|
+
to ten, and stayed shut. The key is refused on a full field; a whole number pasted or
|
|
86
|
+
autofilled is reshaped and the field says `input` again; and the country code a number
|
|
87
|
+
arrives with is dropped, since no NANP area code starts with a 1 — which is what
|
|
88
|
+
fountain's `national` controller did on its own.
|
|
89
|
+
|
|
90
|
+
* [Feature] `notices` draws the flash as toasts
|
|
91
|
+
|
|
92
|
+
Every message in the flash as a toast at the bottom right — green for a notice, red for
|
|
93
|
+
an alert, neutral for a key of the host's own — visible at first paint, held while read
|
|
94
|
+
and gone after a moment, the way recourse shows them. The gem's layout calls it, so a
|
|
95
|
+
page it draws shows what a redirect had to say.
|
|
96
|
+
|
|
97
|
+
* [Feature] `HouseAccount::FormBuilder` is the house's form, `pin_field` included
|
|
98
|
+
|
|
99
|
+
Given as `builder:` or a controller's `default_form_builder`: `fieldset` is the one-column
|
|
100
|
+
grid a form's controls sit in, with a legend where given, a label is a `form-label`, a
|
|
101
|
+
text or phone field a large control, the phone one shaped by the `phone` controller as it
|
|
102
|
+
is typed, a submit or a button the solid pill, a `type: :button` one the outlined pill, and a
|
|
103
|
+
disabled one given a `title` says why on hover — `Available soon` — from a wrapper that
|
|
104
|
+
hears the mouse for it. `pin_field` is one real field rendered by Bootstrap
|
|
105
|
+
as six slots, with the `otp` controller from fountain: the slots drawn on a Turbo visit, a pasted or autofilled code sent on its own,
|
|
106
|
+
a refused one kept with the caret at its end.
|
|
107
|
+
|
|
108
|
+
* [Feature] A `<select data-controller='combobox'>` is Bootstrap's combobox, and any `data-turbo-confirm` asks through a dialog
|
|
109
|
+
|
|
110
|
+
The bundle dresses a select — a toggle, a menu with a search box, a check per pick where
|
|
111
|
+
it is `multiple`, an `All` row, a figure beside an option — and keeps the select as what
|
|
112
|
+
the form submits, announcing a pick as its own `change`. Recourse and any app write a
|
|
113
|
+
select and nothing more. The confirm dialog is built by the bundle on the first ask, so
|
|
114
|
+
no page has to carry the markup, and its answer wears the words of the button that asked.
|
|
115
|
+
|
|
116
|
+
* [Feature] Faster first paint: the icon font cut to the glyphs the house draws, Quicksand served from here, and a page's own version linked and kept for a year
|
|
117
|
+
|
|
118
|
+
Bootstrap Icons shipped two thousand glyphs in 134 KB; `art/icons.txt` names the ones a
|
|
119
|
+
HouseAccount page may draw and `rake icons` cuts the font and its stylesheet to those.
|
|
120
|
+
Quicksand comes from `/font` rather than Google, so no page reaches another origin. The
|
|
121
|
+
engine serves this version's copy under `/v<version>/`, cached immutable, and
|
|
122
|
+
`houseaccount_head_tags` links it, so a returning reader fetches nothing.
|
|
123
|
+
|
|
124
|
+
## 0.2.0 - 2026-09-13
|
|
125
|
+
|
|
126
|
+
* [Feature] The front end of every HouseAccount app, extracted from recourse and the apps
|
|
127
|
+
|
|
128
|
+
One stylesheet and one script — Bootstrap 6 alpha, Bootstrap Icons, Turbo, Stimulus and the
|
|
129
|
+
twenty controllers behind the house's pages — served by the engine at `/css/houseaccount.css`
|
|
130
|
+
and `/js/houseaccount.js`, and published at https://design.houseaccount.com under the same
|
|
131
|
+
paths for a page not on the gem. The nine palettes under `/theme`, every app's mark under
|
|
132
|
+
`/logo` and `/favicon`, and the five error pages under `/page`, which the engine answers a
|
|
133
|
+
failing request with.
|
|
134
|
+
|
|
135
|
+
* [Feature] `houseaccount_head_tags`, `houseaccount_logo`, `chat_with` and `houseaccount_url`
|
|
136
|
+
|
|
137
|
+
A head, a lockup and a thread of bubbles drawn the same way in every app. Which app a host
|
|
138
|
+
is, the gem reads off the Rails application's name, and the head and the down page follow
|
|
139
|
+
it; `houseaccount_logo app: :fountain` draws any of the family's marks seated by its own numbers beside its
|
|
140
|
+
name. `chat_with url:, hints:, messages:` takes messages answering `side`, `text`, `sender`,
|
|
141
|
+
`at`, `delivery` and `cost`, or `HouseAccount::Message`s, and the URL the next one posts to.
|
|
142
|
+
|
|
143
|
+
* [Feature] A layout for a page recourse does not draw
|
|
144
|
+
|
|
145
|
+
`layout 'houseaccount'` on a controller gives a page the shared head — charset, title,
|
|
146
|
+
CSRF and CSP metas, the stylesheet, the script and the icons — then `yield :head`, then
|
|
147
|
+
the page inside `<body>`. `content_for :title` names it; the app's own name stands in.
|
|
148
|
+
|
|
149
|
+
* [Feature] The page an app shows while it is down
|
|
150
|
+
|
|
151
|
+
Its own mark in the middle and the house's at the foot, rendered per app into `/down/<app>.html`
|
|
152
|
+
and answered at `/down.html` by the engine for the app the host named. The design site
|
|
153
|
+
has a mark of its own, a pencil, and wears it.
|
|
154
|
+
|
|
155
|
+
* [Feature] Every release is kept at its own versioned URL
|
|
156
|
+
|
|
157
|
+
`/v0.2.0/css/houseaccount.css` and its script, fonts and palettes stand beside the
|
|
158
|
+
unversioned copies, whole on their own and cached for a year, so a page may pin a
|
|
159
|
+
version and move when it chooses. `rake version` writes a folder once per release.
|
|
160
|
+
|
|
161
|
+
* [Feature] The signup flow's wall of drifting tools, from one attribute
|
|
162
|
+
|
|
163
|
+
`data-controller='wall'` on a page's shell draws the cyanotype of trades and the family's
|
|
164
|
+
marks behind it, from geometry the bundle carries; `.flow-shell`, `.flow-main` and
|
|
165
|
+
`.welcome-card` are the classes the page in front of it wears.
|
|
166
|
+
|
|
167
|
+
* [Feature] A phone is formatted in the browser
|
|
168
|
+
|
|
169
|
+
The server hands over ten digits and `data-controller='phone'`; the controller decides
|
|
170
|
+
`555-555-5555` on a cell and on a field, and gives the field its pattern and placeholder.
|
|
171
|
+
|
|
10
172
|
## 0.1.0 - 2026-08-27
|
|
11
173
|
|
|
12
|
-
*
|
|
174
|
+
* A library that loads and does not do anything yet.
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2026 Claudio Baccigalupo
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
|
@@ -1,36 +1,246 @@
|
|
|
1
|
-
#
|
|
1
|
+
# HouseAccount design
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Everything a HouseAccount page is made of, in one place: the stylesheet and the script every
|
|
4
|
+
page loads, the marks every app is known by, the pages shown when an app is not, and the
|
|
5
|
+
Rails helpers that draw a head, a logo or a chat the same way in every app.
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
Published three ways from this repo:
|
|
8
|
+
|
|
9
|
+
| Surface | Where | What it is for |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| Site | https://design.houseaccount.com (staging: https://design.houseparty.dev) | A stable URL for every asset, and a showcase of all of them |
|
|
12
|
+
| Gem | `houseaccount` on RubyGems | A Rails engine: serves the same tree from the gem, and the helpers |
|
|
13
|
+
| npm | `houseaccount` | The controllers and stylesheets as importable source |
|
|
8
14
|
|
|
9
15
|
## How to install
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
```bash
|
|
18
|
+
gem install houseaccount
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
# Gemfile
|
|
23
|
+
gem 'houseaccount', '~> 0.3.0'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`~> 0.3.0` stops short of `0.4`, since below 1.0 a minor release may break whatever it likes.
|
|
27
|
+
Rails 8.1 and Ruby 3.2 are the minimum.
|
|
28
|
+
|
|
29
|
+
A page takes the gem's layout, and wears the same head:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
class Unauthenticated::AgentsController < ApplicationController
|
|
33
|
+
layout 'houseaccount'
|
|
34
|
+
end
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```erb
|
|
38
|
+
<% content_for :title, 'Authenticating as admin' %>
|
|
39
|
+
<% content_for :head, tag.meta(name: 'robots', content: 'noindex') %>
|
|
40
|
+
<h1>Authenticating as admin</h1>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
<p>
|
|
44
|
+
<img src='screenshot/layout-light.png' width='49%' alt='The page in light mode'>
|
|
45
|
+
<img src='screenshot/layout-dark.png' width='49%' alt='The page in dark mode'>
|
|
46
|
+
</p>
|
|
47
|
+
|
|
48
|
+
The layout is the charset, the title, the CSRF and CSP metas, `houseaccount_head_tags`,
|
|
49
|
+
`yield :head`, then `yield` inside a `main.container-fluid` in the `<body>`, and `notices`
|
|
50
|
+
after it, and nothing else. A host wanting more copies
|
|
51
|
+
it into its own `app/views/layouts/`, which wins on being earlier in the view paths.
|
|
52
|
+
|
|
53
|
+
Which of the family an app is, the gem reads off the Rails application's own name: the
|
|
54
|
+
`Goldrush` of `Goldrush::Application`. That is what its tab wears, what `houseaccount_head_tags`
|
|
55
|
+
links without an argument, and whose page the engine answers `/down.html` with — the app's
|
|
56
|
+
mark in the middle and the house's at the foot. An app the family has no mark for wears the
|
|
57
|
+
cube. The same page stands at `https://design.houseaccount.com/down/fountain` for Heroku's
|
|
58
|
+
`MAINTENANCE_PAGE_URL`, which has to be somewhere the app is not — without the `.html`,
|
|
59
|
+
which Pages redirects away from.
|
|
60
|
+
|
|
61
|
+
With the gem in the bundle an app serves `/css/houseaccount.css`, `/js/houseaccount.js`,
|
|
62
|
+
`/theme/*`, `/font/*`, `/logo/*` and `/favicon/*` itself — and its own version's copy under
|
|
63
|
+
`/v0.3.0/`, which is what the head helper links, cached for a year — answers a 400, 404, 406, 422 or 500
|
|
64
|
+
with the house's own page, and has these helpers in every view:
|
|
65
|
+
|
|
66
|
+
```erb
|
|
67
|
+
<%# In the <head>: the viewport, color-scheme and Turbo refresh metas, the stylesheet, the script,
|
|
68
|
+
and the icons a tab, a phone and a manifest ask for — this app's, by its name. %>
|
|
69
|
+
<%= houseaccount_head_tags %>
|
|
70
|
+
|
|
71
|
+
<%# A mark beside its name, as one word and one link. The house's cube and `houseaccount`
|
|
72
|
+
leading to houseaccount.com by default, the way a page signs itself in a footer; any
|
|
73
|
+
app's own with its name and wherever `href` says. Other options are attributes of the
|
|
74
|
+
link. Under the cursor the whole logo turns the mark's blue. %>
|
|
75
|
+
<%= houseaccount_logo %>
|
|
76
|
+
<%= houseaccount_logo app: :fountain, href: root_path, class: 'fs-3' %>
|
|
77
|
+
|
|
78
|
+
<%# A thread of messages, newest at the foot, and — where `url` is given — the field that
|
|
79
|
+
posts the next one there, suggesting `hints` one at a time in its placeholder. %>
|
|
80
|
+
<%= chat_with url: chat_question_path, hints: Provider::Chat::HINTS, messages: messages %>
|
|
81
|
+
|
|
82
|
+
<%# What the flash says, as toasts at the bottom right: green for a notice, red for an alert,
|
|
83
|
+
neutral for any other key; visible at first paint, gone after a moment or a click, held
|
|
84
|
+
while read. A value in the flash that is not words is left alone. %>
|
|
85
|
+
<%= notices %>
|
|
86
|
+
|
|
87
|
+
<%# A link and the dialog it opens: headed, with an X, holding the block, and closed by an
|
|
88
|
+
`Okay` too (`okay:` words it otherwise, `okay: false` leaves it out). The two share an id
|
|
89
|
+
made from the link's words; any other option is an attribute of the link. %>
|
|
90
|
+
<%= dialog 'I’m not on Google Business', title: 'Not a problem' do %>
|
|
91
|
+
We will look your business up ourselves and match it by hand.
|
|
92
|
+
<% end %>
|
|
93
|
+
|
|
94
|
+
<%# The house's form, from `builder: HouseAccount::FormBuilder` or a controller's
|
|
95
|
+
`default_form_builder`: `fieldset` a one-column grid with a legend where given, a label a
|
|
96
|
+
form-label, a text or phone field a large control — the phone one shaped by the `phone`
|
|
97
|
+
controller — a submit or a button the solid pill, a `type: :button` one the outlined pill, a
|
|
98
|
+
disabled one given a `title` saying why on hover, and
|
|
99
|
+
`pin_field` the six slots a code is typed into, one real field offered by the phone from the
|
|
100
|
+
text that brought it. %>
|
|
101
|
+
<%= form.fieldset do %>
|
|
102
|
+
<%= form.label :pin_confirmation, '6-digit code' %>
|
|
103
|
+
<%= form.pin_field :pin_confirmation, required: true %>
|
|
104
|
+
<%= form.submit 'Confirm' %>
|
|
105
|
+
<% end %>
|
|
106
|
+
|
|
107
|
+
<%# An asset on the design site, for a page this app does not serve — a mail, say. %>
|
|
108
|
+
<%= image_tag houseaccount_url('logo/fountain.png') %>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Two more behaviors need no helper either, only an attribute. A `<select>` becomes Bootstrap's
|
|
112
|
+
combobox — a toggle reading the pick, a menu with a search box, a check per pick where it
|
|
113
|
+
is `multiple` — and stays the one thing the form submits:
|
|
12
114
|
|
|
13
|
-
|
|
115
|
+
```erb
|
|
116
|
+
<%= f.select :team_id, Team.pluck(:name, :id), { include_blank: 'None' },
|
|
117
|
+
data: { controller: 'combobox', combobox_placeholder_value: 'Select…',
|
|
118
|
+
search: 'Search…', clear: 'Clear search', no_results: 'No results found' } %>
|
|
119
|
+
```
|
|
14
120
|
|
|
15
|
-
|
|
121
|
+
On a `multiple` select, `data-combobox-all-value` names an `All` row that puts held-back
|
|
122
|
+
options on the menu and `data-combobox-more-value` words the toggle when several are picked
|
|
123
|
+
(`%{first} + %{count} more`); on an option, `data-count` puts a figure beside it and
|
|
124
|
+
`data-hidden` holds it back until `All`. A pick is announced as the select's own `change`.
|
|
16
125
|
|
|
17
|
-
|
|
126
|
+
And any link or form with `data-turbo-confirm` asks through a Bootstrap dialog the bundle
|
|
127
|
+
draws on the first ask: the first line of the message is the question, the rest are
|
|
128
|
+
paragraphs, and the answer wears the words of the button that asked. A host in another
|
|
129
|
+
language names the other word in `<meta name='houseaccount-cancel'>`.
|
|
18
130
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
131
|
+
`flow` draws the page of a signup flow around a card: the mark over the wall, a title, a
|
|
132
|
+
line under it, the card, and the way out at the foot. A layout of the app's own calls it and
|
|
133
|
+
nests on the gem's layout, giving the page as `content_for :content` and rendering the gem's
|
|
134
|
+
as a template:
|
|
135
|
+
|
|
136
|
+
```erb
|
|
137
|
+
<% content_for :content do %>
|
|
138
|
+
<%= flow logo: root_path, footer: [button_to('Sign out', session_path, method: :delete, class: 'btn btn-link')] do %>
|
|
139
|
+
<%= yield %>
|
|
140
|
+
<% end %>
|
|
141
|
+
<% end %>
|
|
142
|
+
|
|
143
|
+
<%= render template: 'layouts/houseaccount' %>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The words are the view's, said in `content_for`: `:title` is the `h1`, `:subtitle` the line
|
|
147
|
+
under it, drawn only where there is one, and `:width` how wide the card is, a CSS length that
|
|
148
|
+
is `30rem` unless said — a chat's page says `75rem`. What the layout knows goes in the call:
|
|
149
|
+
`footer:` is the pieces of the foot, joined by a middot, none drawing no foot, and `logo:`
|
|
150
|
+
where the mark links, the house's front door unless said.
|
|
151
|
+
|
|
152
|
+
The wall alone is one attribute, `data-controller='wall'`, on any element that should
|
|
153
|
+
stand on it.
|
|
154
|
+
|
|
155
|
+
A message is anything answering six methods, and `HouseAccount::Message` is one for a host
|
|
156
|
+
that has the facts and no model to hang them on:
|
|
157
|
+
|
|
158
|
+
| Method | What it says |
|
|
159
|
+
| --- | --- |
|
|
160
|
+
| `side` | `:left` for the other party's bubble, `:right` for the reader's |
|
|
161
|
+
| `text` | what was said — a string, or markup the host rendered; nil draws the typing dots |
|
|
162
|
+
| `sender` | who said it, shown small above the bubble; nil shows nobody |
|
|
163
|
+
| `at` | when, as `3 minutes ago` with the moment itself in a tooltip; nil shows no time |
|
|
164
|
+
| `delivery` | `:delivered`, `:sent`, `:failed` or `:undelivered`, a ✓ or ✕ in its tone; nil for none |
|
|
165
|
+
| `cost` | dollars the message cost, shown as `· $0.0123`; nil for none |
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
HouseAccount::Message.new side: :left, text: answer.note, sender: 'Claude', at: answer.created_at,
|
|
169
|
+
cost: Chat.spent_on(answer.input_usage, answer.output_usage)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
A code texted to a phone is the same words from every app, `HouseAccount.one_time_code`:
|
|
173
|
+
the sentence a person reads, and under it the `@host #code` line that offers the code above
|
|
174
|
+
the keyboard on the phone it arrived on. The host is the application's default one unless
|
|
175
|
+
`host:` says otherwise, and the copy is the gem's `config/locales` entry.
|
|
176
|
+
|
|
177
|
+
```ruby
|
|
178
|
+
HouseAccount.one_time_code contact.pin
|
|
179
|
+
# => "Enter this 6-digit code to proceed: 482913\n\n@houseaccount.com #482913"
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
A page not on the gem links the two files from the site, and nothing else:
|
|
183
|
+
|
|
184
|
+
```html
|
|
185
|
+
<link rel='stylesheet' href='https://design.houseaccount.com/css/houseaccount.css'>
|
|
186
|
+
<script type='module' src='https://design.houseaccount.com/js/houseaccount.js'></script>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Those follow the latest release. Every release is also kept whole under its own version,
|
|
190
|
+
forever and cached for a year, for a page that would rather move on its own terms:
|
|
191
|
+
|
|
192
|
+
```html
|
|
193
|
+
<link rel='stylesheet' href='https://design.houseaccount.com/v0.3.0/css/houseaccount.css'>
|
|
194
|
+
<script type='module' src='https://design.houseaccount.com/v0.3.0/js/houseaccount.js'></script>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
A host with a Content Security Policy allows that origin for `style-src`, `script-src` and
|
|
198
|
+
`font-src`.
|
|
199
|
+
|
|
200
|
+
## What is in the bundle
|
|
201
|
+
|
|
202
|
+
`css/houseaccount.css` is Bootstrap 6 alpha as upstream ships it, Bootstrap Icons cut down to
|
|
203
|
+
the glyphs `art/icons.txt` names — the ones `unicon` maps and the few written out by name —
|
|
204
|
+
and then the house's rules by concern under `app/stylesheets/houseaccount/`. Quicksand, the
|
|
205
|
+
lockup's face, is served from `/font` too; nothing on a page reaches another origin. `js/houseaccount.js` is
|
|
206
|
+
Turbo (with the cable element `turbo-rails` signs its streams for), Bootstrap, and a Stimulus
|
|
207
|
+
application with every controller the house's markup names, started once under
|
|
208
|
+
`window.Stimulus`. Everything is drawn for light and dark alike: every color is a Bootstrap
|
|
209
|
+
token or a `light-dark()` pair, and `data-bs-theme` on `<html>` forces either. A phone is
|
|
210
|
+
formatted in the browser: the server hands over ten digits and
|
|
211
|
+
`data-controller='phone'`, and the controller decides `555-555-5555`.
|
|
22
212
|
|
|
23
213
|
## Development
|
|
24
214
|
|
|
25
|
-
|
|
26
|
-
|
|
215
|
+
```bash
|
|
216
|
+
bin/setup # bundle and npm install
|
|
217
|
+
bundle exec rake # build, test at 100% coverage, RuboCop, the file-length ceiling
|
|
218
|
+
./build-icons.sh # after editing a drawing under art/
|
|
219
|
+
rake icons # after editing art/icons.txt, the glyphs the icon font keeps
|
|
220
|
+
cd test/dummy && bin/rails server
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The built `public/css`, `public/js`, `public/font` and `public/theme` are committed: the gem
|
|
224
|
+
serves them, and `spec.files` is `git ls-files`. `rake assets` rebuilds and fails until the
|
|
225
|
+
result is committed, so a source edit cannot ship without its build.
|
|
226
|
+
|
|
227
|
+
While the gem is read from a path, an app in development serves every file `no-cache`, so a
|
|
228
|
+
rebuild shows on the next reload; only a production app keeps the versioned copy for a year.
|
|
27
229
|
|
|
28
|
-
|
|
230
|
+
Every build also writes `public/v<version>/`, whole, with every URL inside pointing under
|
|
231
|
+
that prefix, until the tag `v<version>` exists; from then on the folder is never written
|
|
232
|
+
again, and a page that pinned it is owed the same bytes for good.
|
|
29
233
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
234
|
+
A release, in order: `CHANGELOG.md`, the version in `lib/houseaccount/version.rb` and
|
|
235
|
+
`package.json`, `bundle exec rake` so the versioned copy is the release's, then commit, tag
|
|
236
|
+
`v<version>`, `gem build` and `gem push`, and fast-forward `production` to `main` so the
|
|
237
|
+
site carries both the latest and the versioned copy.
|
|
33
238
|
|
|
34
|
-
##
|
|
239
|
+
## Cloudflare
|
|
35
240
|
|
|
36
|
-
|
|
241
|
+
The site is a Cloudflare Pages project named `design` on this repo: production branch
|
|
242
|
+
`production`, no build command, output directory `public`, custom domain
|
|
243
|
+
`design.houseaccount.com`. `main` deploys as the branch alias `main.design.pages.dev`, which
|
|
244
|
+
`design.houseparty.dev` is a CNAME to. `public/_headers` adds the CORS header a cross-origin
|
|
245
|
+
module script and font need, and marks the hashed fonts immutable; everything else keeps
|
|
246
|
+
Pages' default of revalidating on every load, so a URL never has to change between versions.
|
data/art/autopilot.svg
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
2
|
+
<g fill="none" stroke="#3b8ae8" stroke-width="28" stroke-linecap="round" stroke-linejoin="round">
|
|
3
|
+
<circle cx="256" cy="256" r="190"/>
|
|
4
|
+
<path d="M66 256 L206 256"/>
|
|
5
|
+
<path d="M306 256 L446 256"/>
|
|
6
|
+
<path d="M256 306 L256 446"/>
|
|
7
|
+
<circle cx="256" cy="256" r="50"/>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
data/art/design.svg
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
2
|
+
<g fill="none" stroke="#3b8ae8" stroke-width="28" stroke-linecap="round" stroke-linejoin="round" transform="rotate(45 256 256)">
|
|
3
|
+
<path d="M196 60 L316 60 L316 372 L196 372 Z"/>
|
|
4
|
+
<path d="M196 120 L316 120"/>
|
|
5
|
+
<path d="M196 372 L256 470 L316 372"/>
|
|
6
|
+
<path d="M232 431 L256 470 L280 431 Z" fill="#1B5288"/>
|
|
7
|
+
</g>
|
|
8
|
+
</svg>
|
data/art/down.html.erb
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<%# The page an app shows while it is down: its own mark and name in the middle, the house's
|
|
2
|
+
at the foot — or the house's alone, for an app not in the family — and nothing that
|
|
3
|
+
needs the app to be up — Quicksand from Google, the icons
|
|
4
|
+
from the design site, the two blues written in. Rendered per app by `rake pages` into
|
|
5
|
+
public/down/<app>.html, and answered at /down.html by the engine once the host has named
|
|
6
|
+
itself in `Houseaccount.app`. -%>
|
|
7
|
+
<!doctype html>
|
|
8
|
+
<html lang='en'>
|
|
9
|
+
<head>
|
|
10
|
+
<meta charset='utf-8'>
|
|
11
|
+
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
|
12
|
+
<title><%= app %></title>
|
|
13
|
+
<meta name='color-scheme' content='light dark'>
|
|
14
|
+
<link rel='icon' href='https://design.houseaccount.com/favicon/<%= app %>.svg' type='image/svg+xml'>
|
|
15
|
+
<link rel='icon' href='https://design.houseaccount.com/favicon/<%= app %>-96.png' type='image/png' sizes='96x96'>
|
|
16
|
+
<link rel='apple-touch-icon' href='https://design.houseaccount.com/favicon/<%= app %>-180.png'>
|
|
17
|
+
<style>
|
|
18
|
+
@font-face {
|
|
19
|
+
font-family: 'Quicksand'; font-style: normal; font-weight: 500; font-display: swap;
|
|
20
|
+
src: url('https://design.houseaccount.com/font/quicksand-500-vietnamese.woff2') format('woff2');
|
|
21
|
+
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
|
22
|
+
}
|
|
23
|
+
@font-face {
|
|
24
|
+
font-family: 'Quicksand'; font-style: normal; font-weight: 500; font-display: swap;
|
|
25
|
+
src: url('https://design.houseaccount.com/font/quicksand-500-latin-ext.woff2') format('woff2');
|
|
26
|
+
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
27
|
+
}
|
|
28
|
+
@font-face {
|
|
29
|
+
font-family: 'Quicksand'; font-style: normal; font-weight: 500; font-display: swap;
|
|
30
|
+
src: url('https://design.houseaccount.com/font/quicksand-500-latin.woff2') format('woff2');
|
|
31
|
+
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
32
|
+
}
|
|
33
|
+
:root {
|
|
34
|
+
--bg: #fbfcfe;
|
|
35
|
+
--ink: #1B5288;
|
|
36
|
+
--mark: #3b8ae8;
|
|
37
|
+
}
|
|
38
|
+
@media (prefers-color-scheme: dark) {
|
|
39
|
+
:root {
|
|
40
|
+
--bg: #0f1622;
|
|
41
|
+
--ink: #9cc0ec;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
* { box-sizing: border-box; }
|
|
45
|
+
body {
|
|
46
|
+
margin: 0;
|
|
47
|
+
min-height: 100svh;
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
background: var(--bg);
|
|
51
|
+
font-family: 'Quicksand', 'Trebuchet MS', sans-serif;
|
|
52
|
+
font-weight: 500;
|
|
53
|
+
letter-spacing: 0.01em;
|
|
54
|
+
}
|
|
55
|
+
main {
|
|
56
|
+
flex: 1;
|
|
57
|
+
display: grid;
|
|
58
|
+
place-items: center;
|
|
59
|
+
}
|
|
60
|
+
.lockup {
|
|
61
|
+
white-space: nowrap;
|
|
62
|
+
color: var(--ink);
|
|
63
|
+
line-height: 1;
|
|
64
|
+
}
|
|
65
|
+
/* The mark on the text baseline, sized and nudged by its own numbers, in ems so the
|
|
66
|
+
same pair reads right at both sizes on this page. */
|
|
67
|
+
.lockup svg {
|
|
68
|
+
position: relative;
|
|
69
|
+
vertical-align: baseline;
|
|
70
|
+
margin-right: 0.29em;
|
|
71
|
+
}
|
|
72
|
+
main .lockup { font-size: 48px; }
|
|
73
|
+
footer {
|
|
74
|
+
position: sticky;
|
|
75
|
+
bottom: 0;
|
|
76
|
+
background: var(--bg);
|
|
77
|
+
display: flex;
|
|
78
|
+
justify-content: center;
|
|
79
|
+
padding: 20px 0 26px;
|
|
80
|
+
}
|
|
81
|
+
footer .lockup { font-size: 20px; }
|
|
82
|
+
footer .house { color: var(--mark); }
|
|
83
|
+
</style>
|
|
84
|
+
</head>
|
|
85
|
+
<body>
|
|
86
|
+
<main>
|
|
87
|
+
<p class='lockup'><%= mark[app] %><%= app == :houseaccount ? "<span class='house'>house</span>account" : app %></p>
|
|
88
|
+
</main>
|
|
89
|
+
<%- unless app == :houseaccount -%>
|
|
90
|
+
<footer>
|
|
91
|
+
<p class='lockup'><%= mark[:houseaccount] %><span class='house'>house</span>account</p>
|
|
92
|
+
</footer>
|
|
93
|
+
<%- end -%>
|
|
94
|
+
</body>
|
|
95
|
+
</html>
|
data/art/fountain.svg
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
2
|
+
<g fill="none" stroke="#3b8ae8" stroke-width="28" stroke-linecap="round">
|
|
3
|
+
<path d="M256 472V40"/>
|
|
4
|
+
<path d="M256 472C234 310 180 202 121 143"/>
|
|
5
|
+
<path d="M256 472C278 310 332 202 391 143"/>
|
|
6
|
+
<path d="M256 472C216 348 124 283 51 251"/>
|
|
7
|
+
<path d="M256 472C296 348 388 283 461 251"/>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
data/art/goldrush.svg
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
2
|
+
<g fill="none" stroke="#3b8ae8" stroke-width="28" stroke-linecap="round" stroke-linejoin="round">
|
|
3
|
+
<path d="M152 248 L190 128 L322 128 L360 248 Z"/>
|
|
4
|
+
<path d="M40 384 L78 264 L210 264 L248 384 Z"/>
|
|
5
|
+
<path d="M264 384 L302 264 L434 264 L472 384 Z"/>
|
|
6
|
+
</g>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
2
|
+
<g fill="none" stroke="#3b8ae8" stroke-width="28" stroke-linecap="round" stroke-linejoin="round">
|
|
3
|
+
<path d="M256 302Q256 256 295.8 233L406.2 169.3Q446 146.3 446 192.3V319.7Q446 365.7 406.2 388.7L295.8 452.4Q256 475.4 256 429.4Z" fill="#1B5288"/>
|
|
4
|
+
<path d="M216.2 59.6Q256 36.6 295.8 59.6L406.2 123.3Q446 146.3 446 192.3V319.7Q446 365.7 406.2 388.7L295.8 452.4Q256 475.4 216.2 452.4L105.8 388.7Q66 365.7 66 319.7V192.3Q66 146.3 105.8 123.3Z"/>
|
|
5
|
+
<path d="M256 256 95.4 163.3"/>
|
|
6
|
+
<path d="M256 256 416.6 163.3"/>
|
|
7
|
+
<path d="M256 256V441.4"/>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
data/art/houston.svg
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
2
|
+
<g fill="none" stroke="#3b8ae8" stroke-width="28" stroke-linecap="round">
|
|
3
|
+
<circle cx="256" cy="256" r="224"/>
|
|
4
|
+
<path d="M32 256h448"/>
|
|
5
|
+
<path d="M256 32c142 63 142 385 0 448"/>
|
|
6
|
+
<path d="M256 32c-142 63-142 385 0 448"/>
|
|
7
|
+
</g>
|
|
8
|
+
</svg>
|