head 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +37 -0
  5. data/app/assets/fonts/head/headicons.woff +0 -0
  6. data/app/assets/fonts/icomoon.json +228 -0
  7. data/app/assets/javascript/head/index.js +2 -0
  8. data/app/assets/javascript/head/knob.js +51 -0
  9. data/app/assets/javascript/head/wing.js +18 -0
  10. data/app/assets/stylesheets/head/components/knob.sass +49 -0
  11. data/app/assets/stylesheets/head/components/theater.sass +55 -0
  12. data/app/assets/stylesheets/head/components/wing.sass +70 -0
  13. data/app/assets/stylesheets/head/generics/flip.sass +3 -0
  14. data/app/assets/stylesheets/head/generics/fonts.sass +6 -0
  15. data/app/assets/stylesheets/head/index.sass +12 -0
  16. data/app/assets/stylesheets/head/objects/headicon.sass +28 -0
  17. data/app/assets/stylesheets/head/package.json +5 -0
  18. data/app/assets/stylesheets/head/settings/_colors.sass +14 -0
  19. data/app/assets/stylesheets/head/settings/_icons.scss +7 -0
  20. data/app/assets/stylesheets/head/tools/_cloud.sass +17 -0
  21. data/app/assets/stylesheets/head/tools/_link.sass +3 -0
  22. data/app/assets/stylesheets/head/tools/_mandatory.sass +6 -0
  23. data/app/assets/stylesheets/head/tools/_terminal.sass +3 -0
  24. data/app/assets/stylesheets/head/tools/_theme.sass +3 -0
  25. data/app/assets/stylesheets/head/utilities/smokescreen.sass +5 -0
  26. data/app/components/head/application_component.rb +39 -0
  27. data/app/components/head/knob.rb +86 -0
  28. data/app/components/head/theater.rb +87 -0
  29. data/app/components/head/wing.rb +37 -0
  30. data/config/importmap.rb +6 -0
  31. data/config/locales/head.en.yml +1 -0
  32. data/lib/head/css.rb +10 -0
  33. data/lib/head/engine.rb +37 -0
  34. data/lib/head/identicon.rb +53 -0
  35. data/lib/head/version.rb +5 -0
  36. data/lib/head.rb +10 -0
  37. metadata +177 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 11835ad46d870dcdc415a869c67328331f67eff34b8d1e704c2df9b184f48e30
4
+ data.tar.gz: f913154fbd0cb1b807fec777b50a736b25352232d88d67ea60d2ecc9abc489e2
5
+ SHA512:
6
+ metadata.gz: a79743be804f0101a40d6696fd4266641ac1741bd7451d2e5558ff6b6b90160c6461bb40124b4c8938a903e0101193ff383c7099a7df01c3ba8fa56df961fc92
7
+ data.tar.gz: 4ec8047baec015c2a05d96914ce983f81e2c4e3c4316980b86d4226a00948d8816039512f4a9a6a8a3a61a5360dbf0f6f266d8b6b734933a29fce3eee7df22cb
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.0.1] - 2025-10-02
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) halo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # Head
2
+
3
+ Opinionated ViewComponents for building forms.
4
+
5
+ ## Usage
6
+
7
+ ```ruby
8
+ # In Rails view
9
+ ```
10
+
11
+ ## Installation
12
+
13
+ Install the gem and add to the application's Gemfile by executing:
14
+
15
+ bundle add head
16
+
17
+ If bundler is not being used to manage dependencies, install the gem by executing:
18
+
19
+ gem install head
20
+
21
+ ## Development
22
+
23
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/ci` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
24
+
25
+ ## Testing
26
+
27
+ Run one test individually with:
28
+
29
+ bundle exec ruby -I test test/lib/wrappers/test_required.rb
30
+
31
+ ## Contributing
32
+
33
+ Bug reports and pull requests are welcome on GitHub at https://github.com/halo/head.
34
+
35
+ ## License
36
+
37
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,228 @@
1
+ {
2
+ "metadata": {
3
+ "name": "headicons",
4
+ "lastOpened": 0,
5
+ "created": 1759221184557
6
+ },
7
+ "iconSets": [
8
+ {
9
+ "selection": [
10
+ {
11
+ "order": 9,
12
+ "id": 7,
13
+ "name": "gear",
14
+ "prevSize": 32,
15
+ "code": 59654,
16
+ "tempChar": ""
17
+ },
18
+ {
19
+ "order": 8,
20
+ "id": 6,
21
+ "name": "question-mark",
22
+ "prevSize": 32,
23
+ "code": 59648,
24
+ "tempChar": ""
25
+ },
26
+ {
27
+ "order": 7,
28
+ "id": 5,
29
+ "name": "bell-ringing",
30
+ "prevSize": 32,
31
+ "code": 59649,
32
+ "tempChar": ""
33
+ },
34
+ {
35
+ "order": 6,
36
+ "id": 4,
37
+ "name": "bell",
38
+ "prevSize": 32,
39
+ "code": 59650,
40
+ "tempChar": ""
41
+ },
42
+ {
43
+ "order": 5,
44
+ "id": 3,
45
+ "name": "magnifier",
46
+ "prevSize": 32,
47
+ "code": 59651,
48
+ "tempChar": ""
49
+ },
50
+ {
51
+ "order": 4,
52
+ "id": 2,
53
+ "name": "bars-thin",
54
+ "prevSize": 32,
55
+ "code": 59652,
56
+ "tempChar": ""
57
+ },
58
+ {
59
+ "order": 3,
60
+ "id": 1,
61
+ "name": "bars-bold",
62
+ "prevSize": 32,
63
+ "code": 59653,
64
+ "tempChar": ""
65
+ }
66
+ ],
67
+ "id": 0,
68
+ "metadata": {
69
+ "name": "Untitled Set",
70
+ "importSize": {
71
+ "width": 24,
72
+ "height": 24
73
+ }
74
+ },
75
+ "height": 1024,
76
+ "prevSize": 32,
77
+ "icons": [
78
+ {
79
+ "id": 7,
80
+ "paths": [
81
+ "M1024 580.949v-137.899c-70.443-25.045-114.944-32.085-137.344-86.144v-0.043c-22.485-54.229 4.267-91.051 36.139-158.165l-97.493-97.493c-66.603 31.659-103.808 58.667-158.165 36.139h-0.043c-54.144-22.443-61.227-67.243-86.144-137.344h-137.899c-24.832 69.76-31.957 114.859-86.144 137.344h-0.043c-54.229 22.528-90.965-4.181-158.165-36.139l-97.493 97.493c31.787 66.901 58.667 103.851 36.139 158.165-22.485 54.229-67.584 61.355-137.344 86.187v137.899c69.632 24.747 114.859 31.957 137.344 86.144 22.613 54.699-4.864 92.416-36.139 158.165l97.493 97.536c66.645-31.701 103.851-58.667 158.165-36.139h0.043c54.187 22.443 61.269 67.371 86.144 137.344h137.899c24.832-69.803 32-114.773 86.485-137.472h0.043c53.845-22.357 90.453 4.309 157.781 36.309l97.493-97.536c-31.744-66.688-58.667-103.808-36.181-158.123 22.485-54.229 67.755-61.44 137.429-86.229zM512 682.667c-94.251 0-170.667-76.416-170.667-170.667s76.416-170.667 170.667-170.667 170.667 76.416 170.667 170.667-76.416 170.667-170.667 170.667z"
82
+ ],
83
+ "attrs": [
84
+ {}
85
+ ],
86
+ "isMulticolor": false,
87
+ "isMulticolor2": false,
88
+ "grid": 0,
89
+ "tags": [
90
+ "gear"
91
+ ]
92
+ },
93
+ {
94
+ "id": 6,
95
+ "paths": [
96
+ "M622.976 917.333c0 58.88-47.616 106.667-106.624 106.667-58.795 0-106.624-47.787-106.624-106.667s47.829-106.667 106.624-106.667c59.008 0 106.624 47.744 106.624 106.667zM519.723 0c-171.904 0-301.227 114.901-301.227 341.333h168.747c0-98.304 38.656-178.731 129.024-178.731 53.205 0 109.653 35.328 114.517 102.869 5.248 71.083-32.725 107.136-80.725 152.832-124.757 118.613-120.149 172.757-120.149 307.029h168.235c0-61.952-6.699-107.008 78.421-198.784 56.789-61.269 127.403-137.472 128.896-253.568 2.005-169.088-117.376-272.981-285.739-272.981z"
97
+ ],
98
+ "attrs": [
99
+ {}
100
+ ],
101
+ "isMulticolor": false,
102
+ "isMulticolor2": false,
103
+ "grid": 0,
104
+ "tags": [
105
+ "question-mark"
106
+ ]
107
+ },
108
+ {
109
+ "id": 5,
110
+ "paths": [
111
+ "M645.845 168.32c-27.477-15.957-44.459-45.653-44.416-77.653v-0.128c0.043-50.005-40.021-90.539-89.429-90.539s-89.472 40.533-89.472 90.539v0.128c0.043 32.043-16.896 61.696-44.416 77.653-199.125 115.712-84.693 499.84-292.779 567.723v74.624h853.333v-74.624c-208.085-67.883-93.653-452.011-292.821-567.723zM512 42.667c23.552 0 42.667 19.157 42.667 42.667 0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667c0-23.509 19.115-42.667 42.667-42.667zM640 896c0 68.181-59.392 128-126.763 128s-129.237-59.819-129.237-128h256zM886.528 397.696c7.68 30.763 2.133 61.696-12.971 86.827l41.387 24.917c21.504-35.755 29.355-79.744 18.475-123.392-10.88-43.691-38.4-78.848-74.197-100.309l-24.832 41.387c25.131 15.147 44.501 39.851 52.139 70.571zM903.424 212.267l-25.472 42.453c43.648 26.283 77.312 69.248 90.667 122.624 13.269 53.376 3.627 107.093-22.613 150.784l42.411 25.515c22.869-38.059 35.627-82.176 35.627-128-0.043-84.48-43.093-166.784-120.619-213.376zM189.611 327.125l-24.832-41.387c-35.797 21.504-63.317 56.619-74.197 100.309-10.88 43.648-3.029 87.595 18.475 123.392l41.387-24.917c-15.104-25.088-20.651-56.064-12.971-86.827 7.637-30.72 27.008-55.424 52.139-70.571zM-0 425.6c0 45.824 12.757 89.941 35.627 128l42.411-25.515c-26.24-43.691-35.883-97.408-22.613-150.784 13.312-53.376 46.976-96.341 90.624-122.581l-25.472-42.453c-77.525 46.592-120.576 128.896-120.576 213.333z"
112
+ ],
113
+ "attrs": [
114
+ {}
115
+ ],
116
+ "isMulticolor": false,
117
+ "isMulticolor2": false,
118
+ "grid": 0,
119
+ "tags": [
120
+ "bell-ringing"
121
+ ]
122
+ },
123
+ {
124
+ "id": 4,
125
+ "paths": [
126
+ "M645.845 168.32c-27.477-15.957-44.459-45.653-44.416-77.653v-0.128c0.043-50.005-40.021-90.539-89.429-90.539s-89.472 40.533-89.472 90.539v0.128c0.043 32.043-16.896 61.696-44.416 77.653-199.125 115.712-84.693 499.84-292.779 567.723v74.624h853.333v-74.624c-208.085-67.883-93.653-452.011-292.821-567.723zM512 42.667c23.552 0 42.667 19.157 42.667 42.667 0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667c0-23.509 19.115-42.667 42.667-42.667zM640 896c0 68.181-59.392 128-126.763 128s-129.237-59.819-129.237-128h256z"
127
+ ],
128
+ "attrs": [
129
+ {}
130
+ ],
131
+ "isMulticolor": false,
132
+ "isMulticolor2": false,
133
+ "grid": 0,
134
+ "tags": [
135
+ "bell"
136
+ ]
137
+ },
138
+ {
139
+ "id": 3,
140
+ "paths": [
141
+ "M1015.851 923.563l-264.747-264.747c49.792-68.48 79.232-152.704 79.232-243.669 0-228.907-186.24-415.147-415.189-415.147-228.907 0-415.147 186.24-415.147 415.147 0 228.949 186.24 415.147 415.147 415.147 86.784 0 167.381-26.752 234.112-72.448l266.155 266.155 100.437-100.437zM121.771 415.147c0-161.792 131.627-293.419 293.419-293.419s293.419 131.627 293.419 293.419-131.627 293.419-293.419 293.419c-161.835 0-293.419-131.627-293.419-293.419z"
142
+ ],
143
+ "attrs": [
144
+ {}
145
+ ],
146
+ "isMulticolor": false,
147
+ "isMulticolor2": false,
148
+ "grid": 0,
149
+ "tags": [
150
+ "magnifier"
151
+ ]
152
+ },
153
+ {
154
+ "id": 2,
155
+ "paths": [
156
+ "M1024 757.333c0-21.205-17.195-38.4-38.4-38.4h-947.2c-21.205 0-38.4 17.195-38.4 38.4s17.195 38.4 38.4 38.4h947.2c21.205 0 38.4-17.195 38.4-38.4zM1024 501.333c0-21.205-17.195-38.4-38.4-38.4h-947.2c-21.205 0-38.4 17.195-38.4 38.4s17.195 38.4 38.4 38.4h947.2c21.205 0 38.4-17.195 38.4-38.4zM1024 245.333c0-21.205-17.195-38.4-38.4-38.4h-947.2c-21.205 0-38.4 17.195-38.4 38.4s17.195 38.4 38.4 38.4h947.2c21.205 0 38.4-17.195 38.4-38.4z"
157
+ ],
158
+ "attrs": [
159
+ {}
160
+ ],
161
+ "isMulticolor": false,
162
+ "isMulticolor2": false,
163
+ "grid": 0,
164
+ "tags": [
165
+ "bars-thin"
166
+ ]
167
+ },
168
+ {
169
+ "id": 1,
170
+ "paths": [
171
+ "M1024 256h-1024v-170.667h1024v170.667zM1024 426.667h-1024v170.667h1024v-170.667zM1024 768h-1024v170.667h1024v-170.667z"
172
+ ],
173
+ "attrs": [
174
+ {}
175
+ ],
176
+ "isMulticolor": false,
177
+ "isMulticolor2": false,
178
+ "grid": 0,
179
+ "tags": [
180
+ "bars-bold"
181
+ ]
182
+ }
183
+ ],
184
+ "colorThemes": [],
185
+ "invisible": false
186
+ }
187
+ ],
188
+ "uid": -1,
189
+ "preferences": {
190
+ "showGlyphs": true,
191
+ "showCodes": true,
192
+ "showQuickUse": true,
193
+ "showQuickUse2": true,
194
+ "showSVGs": true,
195
+ "fontPref": {
196
+ "prefix": "",
197
+ "metadata": {
198
+ "fontFamily": "headicons",
199
+ "majorVersion": 1,
200
+ "minorVersion": 0
201
+ },
202
+ "metrics": {
203
+ "emSize": 1024,
204
+ "baseline": 6.25,
205
+ "whitespace": 50
206
+ },
207
+ "embed": false,
208
+ "noie8": true,
209
+ "ie7": false,
210
+ "cssVars": true,
211
+ "cssVarsFormat": "scss",
212
+ "showSelector": true,
213
+ "selector": "i",
214
+ "showMetrics": true,
215
+ "showMetadata": true,
216
+ "showVersion": true
217
+ },
218
+ "imagePref": {
219
+ "prefix": "icon-",
220
+ "png": true,
221
+ "useClassSelector": true,
222
+ "color": 0,
223
+ "bgColor": 16777215
224
+ },
225
+ "historySize": 50,
226
+ "gridSize": 16
227
+ }
228
+ }
@@ -0,0 +1,2 @@
1
+ import "head/knob"
2
+ import "head/wing"
@@ -0,0 +1,51 @@
1
+ document.addEventListener('DOMContentLoaded', function () {
2
+ document.querySelectorAll('.js-head-knob').forEach((knob) => {
3
+ const href = knob.getAttribute('href')
4
+
5
+ // If this knob doesn't open a sidebar, it is a standalone link.
6
+ if (href != '#') return
7
+
8
+ knob.addEventListener('click', function (event) {
9
+ event.preventDefault()
10
+ const knob = event.currentTarget
11
+
12
+ // The identifier of a knob always matches that of its wing.
13
+ const identifier = knob.dataset.identifier
14
+
15
+ // Check screen size
16
+ const mainMenuWing = document.querySelector(`.js-head-wing[data-identifier="mainmenu"]`)
17
+ const tight = getComputedStyle(mainMenuWing).getPropertyValue('position') == 'absolute'
18
+
19
+ // Handle current wing
20
+ const wing = document.querySelector(`.js-head-wing[data-identifier="${identifier}"]`)
21
+ const visible = getComputedStyle(wing).getPropertyValue('display') == 'block'
22
+ const group = knob.dataset.group
23
+
24
+ // Always close sidebars on the same side of the screen (left/right)
25
+ document.querySelectorAll(`.js-head-wing[data-group="${group}"]`).forEach((someWing) => {
26
+ someWing.classList.remove('is-active')
27
+ someWing.classList.add('is-force-deactive')
28
+ })
29
+
30
+ if (tight) {
31
+ // On small screens, only ever show one menu, so close all others.
32
+ document.querySelectorAll('.js-head-wing').forEach((someWing) => {
33
+ if (someWing != wing) {
34
+ someWing.classList.remove('is-active')
35
+ someWing.classList.add('is-force-deactive')
36
+ }
37
+ })
38
+ }
39
+
40
+ // Now toggle the desired one.
41
+ if (visible) {
42
+ wing.classList.remove('is-active')
43
+ wing.classList.add('is-force-deactive')
44
+ } else {
45
+ wing.classList.add('is-active')
46
+ wing.classList.remove('is-force-deactive')
47
+ }
48
+ })
49
+
50
+ })
51
+ })
@@ -0,0 +1,18 @@
1
+ document.addEventListener('DOMContentLoaded', function() {
2
+
3
+ document.querySelectorAll('.js-head-wing__curtain').forEach(function (item) {
4
+ console.debug("Binding to Head Wing Curtain")
5
+
6
+ item.addEventListener('click', function(event) {
7
+ event.preventDefault()
8
+
9
+ const wing = event.currentTarget.parentNode
10
+ const identifier = wing.dataset.identifier
11
+
12
+ document.querySelectorAll(`.js-head-knob[data-identifier="${identifier}"]`).forEach(function (knob) {
13
+ knob.click()
14
+ })
15
+ })
16
+ })
17
+
18
+ })
@@ -0,0 +1,49 @@
1
+ @use "iglu/font-size"
2
+ @use "iglu/spacing"
3
+ @use "head/settings/colors"
4
+ @use "head/tools/link"
5
+
6
+ .c-head-knob
7
+ display: grid
8
+ padding-left: 0.25em
9
+ padding-right: 0.25em
10
+ text-decoration: none
11
+ +spacing.padding-vertical--tiny
12
+ +font-size.gigantic
13
+
14
+ +link.states
15
+ color: colors.$steel-gray
16
+
17
+ &:hover
18
+ color: colors.$silver-gray
19
+
20
+ &:active
21
+ color: colors.$white
22
+
23
+ &--identicon
24
+ max-width: 1em
25
+ border-radius: 50%
26
+ aspect-ratio: 1
27
+ display: grid
28
+ place-items: center
29
+ overflow: hidden
30
+
31
+ svg
32
+ width: 100%
33
+ height: 100%
34
+ object-fit: contain
35
+
36
+ &--avatar
37
+ max-width: 1em
38
+ border-radius: 50%
39
+ aspect-ratio: 1
40
+ display: grid
41
+ place-items: center
42
+ overflow: hidden
43
+
44
+ img
45
+ width: 100%
46
+ height: 100%
47
+ object-fit: contain
48
+ clip-path: circle()
49
+ background-color: #fff
@@ -0,0 +1,55 @@
1
+ @use "iglu/spacing"
2
+ @use "head/settings/colors"
3
+
4
+ .c-head-theater
5
+ height: 100%
6
+ display: grid
7
+ grid-template-rows: auto 1fr
8
+
9
+ &__roof
10
+ background-color: #444
11
+ display: grid
12
+ align-items: center
13
+ grid-template-columns: auto 1fr auto
14
+ grid-template-areas: "knobs--left logo knobs--right"
15
+
16
+ @each $column in knobs--left logo knobs--right
17
+ &__#{$column}
18
+ grid-area: #{$column}
19
+
20
+ &__knobs
21
+ display: flex
22
+ white-space: nowrap
23
+
24
+ &--left
25
+ +spacing.margin-left--smaller
26
+
27
+ &--right
28
+ justify-content: flex-end
29
+ +spacing.margin-right--smaller
30
+
31
+ &__logo
32
+ display: flex
33
+ justify-content: center
34
+ position: relative
35
+ color: colors.$white
36
+ padding: 0.3rem
37
+ overflow: auto // Clip wide logos on small screens
38
+
39
+ &:active
40
+ top: 1px
41
+
42
+ &__stage
43
+ display: grid
44
+ // So we can track the width of the curtain
45
+ container-type: inline-size
46
+ grid-template-columns: auto 1fr auto
47
+ grid-template-areas: "wings--left content wings--right"
48
+
49
+ &__wings
50
+ &--right
51
+ position: relative
52
+
53
+ @each $column in wings--left content wings--right
54
+ &__#{$column}
55
+ grid-area: #{$column}
@@ -0,0 +1,70 @@
1
+ @use "iglu/responsive"
2
+ @use "head/settings/colors"
3
+
4
+ .c-head-wing
5
+ display: none
6
+ // 320px is the min screen width we support.
7
+ // Our sidebar cannot be bigger, and it needs to leave some space
8
+ // at the right so that a click/tap there can close the sidebar.
9
+ max-width: 265px
10
+ height: 100%
11
+ z-index: 1000
12
+ position: absolute
13
+ background: colors.$granite-gray
14
+ color: colors.$white
15
+
16
+ &--left
17
+ // On rather large screens, always show the main menu in left sidebar
18
+ +responsive.xlarge-container
19
+ position: static
20
+
21
+ &[data-identifier="mainmenu"]
22
+ // We "force" this item to be active without having the `is-active` class.
23
+ display: block
24
+ // So we need some way to deactivate it again using JS.
25
+ &.is-force-deactive
26
+ display: none
27
+
28
+ &--right
29
+ right: 0
30
+
31
+ // On very very large screens, show notifications sidebar on the right.
32
+ +responsive.xxlarge-container
33
+ position: static
34
+
35
+ &[data-identifier="notifications"]
36
+ display: block
37
+
38
+ &.is-force-deactive
39
+ display: none
40
+
41
+ &.is-active
42
+ display: block
43
+
44
+
45
+ &__curtain
46
+ position: absolute
47
+ top: 0
48
+ left: 100%
49
+ width: 100vw
50
+ height: 100%
51
+ z-index: 1001
52
+
53
+ &--right
54
+ right: 100%
55
+ left: inherit
56
+
57
+ +responsive.xlarge-container
58
+ display: none
59
+
60
+
61
+ // +responsive.xxlarge
62
+ // .c-head-wing
63
+
64
+ // // We "force" this item to be active without having the `is-active` class.
65
+ // &[data-identifier="mainmenu"]
66
+ // display: block
67
+
68
+ // // So we need some way to deactivate it again using JS.
69
+ // &.is-force-deactive
70
+ // display: none
@@ -0,0 +1,3 @@
1
+ @keyframes head-flip
2
+ 0%, 80%
3
+ transform: rotateY(360deg)
@@ -0,0 +1,6 @@
1
+ @font-face
2
+ font-family: 'headicons'
3
+ font-weight: normal
4
+ font-style: normal
5
+ src: url('head/headicons.woff') format('woff')
6
+
@@ -0,0 +1,12 @@
1
+ // ITCSS
2
+
3
+ @use "./generics/flip"
4
+ @use "./generics/fonts"
5
+
6
+ @use "./components/theater"
7
+ @use "./components/knob"
8
+ @use "./components/wing"
9
+
10
+ @use "./objects/headicon"
11
+
12
+ @use "./utilities/smokescreen"
@@ -0,0 +1,28 @@
1
+ @use 'head/settings/icons'
2
+
3
+ .o-headicon
4
+ font-family: "headicons"
5
+ speak: none
6
+ font-style: normal
7
+ font-weight: normal
8
+ font-variant: normal
9
+ text-transform: none
10
+
11
+ // &:before
12
+ // vertical-align: middle
13
+
14
+ .o-headicon--bars-bold
15
+ &:before
16
+ content: icons.$bars-bold
17
+
18
+ .o-headicon--magnifier
19
+ &:before
20
+ content: icons.$magnifier
21
+
22
+ .o-headicon--bell
23
+ &:before
24
+ content: icons.$bell
25
+
26
+ .o-headicon--gear
27
+ &:before
28
+ content: icons.$gear
@@ -0,0 +1,5 @@
1
+ {
2
+ "exports": {
3
+ "sass": "head/index.sass"
4
+ }
5
+ }
@@ -0,0 +1,14 @@
1
+ @use "sass:color"
2
+
3
+ $black: #222
4
+ $white: #fff
5
+
6
+ $paradise-pink: rgb(240, 30, 80)
7
+ $neon-yellow: #ffd400
8
+ $jade-green: rgb(0, 190, 130)
9
+
10
+ $granite-gray: rgb(70, 70, 70)
11
+ $steel-gray: color.adjust($granite-gray, $lightness: 25%)
12
+ $silver-gray: color.adjust($granite-gray, $lightness: 50%)
13
+
14
+ $transparent-gray: rgba(100, 100, 100, 0.5)
@@ -0,0 +1,7 @@
1
+ $gear: "\e906";
2
+ $question-mark: "\e900";
3
+ $bell-ringing: "\e901";
4
+ $bell: "\e902";
5
+ $magnifier: "\e903";
6
+ $bars-thin: "\e904";
7
+ $bars-bold: "\e905";
@@ -0,0 +1,17 @@
1
+ =state
2
+ &.is-loading,
3
+ &.is-saved,
4
+ &.is-failed
5
+ background-position: right 0.29rem bottom 0.29rem
6
+ background-repeat: no-repeat
7
+ background-size: 22px 22px
8
+
9
+ &.is-loading
10
+ background-image: url("data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"1024\" height=\"1024\" viewBox=\"0 0 1024 1024\"><path d=\"M512 213.333c147.328 0 251.349 119.339 237.525 289.28 74.453-1.963 189.141 32.043 189.141 158.72 0 82.347-66.987 149.333-149.333 149.333h-554.667c-82.347 0-149.333-66.987-149.333-149.333 0-119.339 105.771-163.541 189.141-158.72-7.125-179.968 94.208-289.28 237.525-289.28zM512 128c-170.923 0-310.059 134.016-319.104 302.592-109.653 19.755-192.896 115.456-192.896 230.741 0 129.579 105.088 234.667 234.667 234.667h554.667c129.579 0 234.667-105.088 234.667-234.667 0-115.285-83.243-210.987-192.896-230.741-9.045-168.576-148.181-302.592-319.104-302.592zM682.667 554.667h-128v170.667h-85.333v-170.667h-128l170.667-170.667 170.667 170.667z\" style=\"fill: rgba(139, 139, 139, 0.243)\"></path></svg>")
11
+
12
+ &.is-saved
13
+ background-image: url("data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"1024\" height=\"1024\" viewBox=\"0 0 1024 1024\"><path d=\"M512 213.333c147.328 0 251.349 119.339 237.525 289.28 74.453-1.963 189.141 32.043 189.141 158.72 0 82.347-66.987 149.333-149.333 149.333h-554.667c-82.347 0-149.333-66.987-149.333-149.333 0-119.339 105.771-163.541 189.141-158.72-7.125-179.968 94.208-289.28 237.525-289.28zM512 128c-170.923 0-310.059 134.016-319.104 302.592-109.653 19.755-192.896 115.456-192.896 230.741 0 129.579 105.088 234.667 234.667 234.667h554.667c129.579 0 234.667-105.088 234.667-234.667 0-115.285-83.243-210.987-192.896-230.741-9.045-168.576-148.181-302.592-319.104-302.592zM682.667 554.667h-128v170.667h-85.333v-170.667h-128l170.667-170.667 170.667 170.667z\" style=\"fill: rgb(0, 163, 0)\"></path></svg>")
14
+
15
+ &.is-failed
16
+ background-image: url("data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"1024\" height=\"1024\" viewBox=\"0 0 1024 1024\"><path d=\"M512 213.333c147.328 0 251.349 119.339 237.525 289.28 74.453-1.963 189.141 32.043 189.141 158.72 0 82.347-66.987 149.333-149.333 149.333h-554.667c-82.347 0-149.333-66.987-149.333-149.333 0-119.339 105.771-163.541 189.141-158.72-7.125-179.968 94.208-289.28 237.525-289.28zM512 128c-170.923 0-310.059 134.016-319.104 302.592-109.653 19.755-192.896 115.456-192.896 230.741 0 129.579 105.088 234.667 234.667 234.667h554.667c129.579 0 234.667-105.088 234.667-234.667 0-115.285-83.243-210.987-192.896-230.741-9.045-168.576-148.181-302.592-319.104-302.592zM682.667 554.667h-128v170.667h-85.333v-170.667h-128l170.667-170.667 170.667 170.667z\" style=\"fill: rgb(185, 1, 1)\"></path></svg>")
17
+ background-color: red
@@ -0,0 +1,3 @@
1
+ =states
2
+ &, &:hover, &:active, &:visited
3
+ @content
@@ -0,0 +1,6 @@
1
+ @use "head/settings/colors"
2
+
3
+ =indicator
4
+ &
5
+ border-left-color: colors.$paradise-pink
6
+ border-left-width: 2px
@@ -0,0 +1,3 @@
1
+ =font
2
+ &
3
+ font-family: Inconsolata, "Roboto Mono", "Source Code Pro", "SF Mono", Monaco, "Fira Mono", "Droid Sans Mono", monospace !important
@@ -0,0 +1,3 @@
1
+ =dark
2
+ @media (prefers-color-scheme: dark)
3
+ @content
@@ -0,0 +1,5 @@
1
+ @use "head/settings/colors"
2
+
3
+ .u-head-smokescreen
4
+ background-color: colors.$transparent-gray
5
+ backdrop-filter: blur(2px)
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Head
4
+ # A component that every other component inherits from in this gem.
5
+ # It adds convenience methods for initialization of a component.
6
+ class ApplicationComponent < ViewComponent::Base
7
+ extend Dry::Initializer
8
+
9
+ # By default, Dry::Initializer doesn't complain about invalid arguments.
10
+ # We want it to raise an error.
11
+ def initialize(...)
12
+ __check_for_unknown_options(...)
13
+ super
14
+ end
15
+
16
+ private
17
+
18
+ def __check_for_unknown_options(*args, **kwargs)
19
+ return if __defined_options.empty?
20
+
21
+ # Checking params
22
+ opts = args.drop(__defined_params.length).first || kwargs
23
+ raise ArgumentError, "Unexpected argument #{opts}" unless opts.is_a? Hash
24
+
25
+ # Checking options
26
+ unknown_options = opts.keys - __defined_options
27
+ message = "Key(s) #{unknown_options} not found in #{__defined_options} of #{self.class}"
28
+ raise KeyError, message if unknown_options.any?
29
+ end
30
+
31
+ def __defined_options
32
+ self.class.dry_initializer.options.map(&:source)
33
+ end
34
+
35
+ def __defined_params
36
+ self.class.dry_initializer.params.map(&:source)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Head
4
+ class Knob < ApplicationComponent
5
+ erb_template <<~ERB
6
+ <%= link_to(url, class: classes, data: { identifier: id, group: group }, **options.except(:url)) do %>
7
+ <% if icon %>
8
+ <i class="o-headicon <%= icon_class%><%= %>"></i>
9
+ <% end %>
10
+
11
+ <%= content %>
12
+ <% end %>
13
+ ERB
14
+
15
+ option :preset, default: -> {}
16
+ option :right, default: -> { false }
17
+ option :id, as: :manual_id, default: -> { false }
18
+ option :icon, as: :manual_icon, default: -> {}
19
+ option :url, as: :manual_url, default: -> {}
20
+ option :options, default: -> { {} }
21
+
22
+ def id
23
+ manual_id || preset
24
+ end
25
+
26
+ def icon
27
+ return manual_icon if manual_icon
28
+ return :'bars-bold' if preset_mainmenu?
29
+ return :bell if preset_notifications?
30
+ return :gear if preset_settings?
31
+
32
+ :magnifier if preset_search?
33
+ end
34
+
35
+ def icon_class
36
+ "o-headicon--#{icon.to_sym.to_s.gsub('_', '-')}"
37
+ end
38
+
39
+ def url
40
+ manual_url || options[:url] || '#'
41
+ end
42
+
43
+ def classes
44
+ result = %w[c-head-knob js-head-knob]
45
+ result.push 'c-head-knob--identicon' if preset_identicon?
46
+ result.push 'c-head-knob--avatar' if preset_avatar?
47
+ result
48
+ end
49
+
50
+ def group
51
+ left? ? :left : :right
52
+ end
53
+
54
+ def left?
55
+ !right?
56
+ end
57
+
58
+ def right?
59
+ @right
60
+ end
61
+
62
+ def preset_mainmenu?
63
+ preset == :mainmenu
64
+ end
65
+
66
+ def preset_search?
67
+ preset == :search
68
+ end
69
+
70
+ def preset_identicon?
71
+ preset == :identicon
72
+ end
73
+
74
+ def preset_avatar?
75
+ preset == :avatar
76
+ end
77
+
78
+ def preset_notifications?
79
+ preset == :notifications
80
+ end
81
+
82
+ def preset_settings?
83
+ preset == :settings
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Head
4
+ class Theater < ApplicationComponent
5
+ erb_template <<~ERB
6
+ <div class="c-head-theater">
7
+ <div class="c-head-theater__roof">
8
+
9
+ <div class="c-head-theater__knobs c-head-theater__knobs--left">
10
+ <% Array(knobs).select(&:left?).each do |knob| %>
11
+ <%= knob %>
12
+ <% end %>
13
+ </div>
14
+
15
+ <div class="c-head-theater__logo">
16
+ <%= logo %>
17
+ </div>
18
+
19
+ <div class="c-head-theater__knobs c-head-theater__knobs--right">
20
+ <% Array(knobs).select(&:right).each do |knob| %>
21
+ <%= knob %>
22
+ <% end %>
23
+ </div>
24
+
25
+ </div>
26
+ <div class="c-head-theater__stage">
27
+
28
+ <div class="c-head-theater__wings c-head-theater__wings--left">
29
+ <%# fail wings.first.content.inspect %>
30
+ <% Array(wings).select(&:left?).each do |wing| %>
31
+ <%= wing %>
32
+ <% end %>
33
+ </div>
34
+
35
+ <div class="c-head-theater__content">
36
+ <%= body %>
37
+ </div>
38
+
39
+ <div class="c-head-theater__wings c-head-theater__wings--right">
40
+ <% Array(wings).select(&:right?).each do |wing| %>
41
+ <%= wing %>
42
+ <% end %>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ ERB
47
+
48
+ def with_mainmenu(&)
49
+ with_knob(preset: :mainmenu)
50
+ with_wing(id: :mainmenu, &)
51
+ end
52
+
53
+ def with_notifications(&)
54
+ with_knob(preset: :notifications, right: true)
55
+ with_wing(id: :notifications, right: true, &)
56
+ end
57
+
58
+ def with_settings(&)
59
+ with_knob(preset: :settings, right: true)
60
+ with_wing(id: :settings, right: true, &)
61
+ end
62
+
63
+ def with_search(**options)
64
+ with_knob(preset: :search, options:)
65
+ end
66
+
67
+ def with_identicon(username:, &)
68
+ with_knob(preset: :identicon, right: true) do
69
+ ::Head::Identicon.new(username.to_s).to_svg
70
+ end
71
+ with_wing(id: :identicon, right: true, &)
72
+ end
73
+
74
+ def with_avatar(image:, &)
75
+ with_knob(preset: :avatar, right: true) do
76
+ image_tag(image)
77
+ end
78
+ with_wing(id: :avatar, right: true, &)
79
+ end
80
+
81
+ renders_many :knobs, ::Head::Knob
82
+ renders_many :wings, ::Head::Wing
83
+
84
+ renders_one :logo
85
+ renders_one :body
86
+ end
87
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Head
4
+ class Wing < ApplicationComponent
5
+ erb_template <<~ERB
6
+ <div class="<%= classes.join(' ') %>" data-identifier="<%= id %>" data-group="<%= group %>">
7
+ <%= content %>
8
+ <div class="c-head-wing__curtain c-head-wing__curtain--<%= group %> js-head-wing__curtain u-head-smokescreen">
9
+ </div>
10
+ </div>
11
+ ERB
12
+
13
+ option :id, as: :id, default: -> {}
14
+ option :right, default: -> { false }
15
+
16
+ def self.mainmenu
17
+ new(id: :mainmenu)
18
+ end
19
+
20
+
21
+ def classes
22
+ %W[c-head-wing c-head-wing--#{group} js-head-wing]
23
+ end
24
+
25
+ def group
26
+ left? ? :left : :right
27
+ end
28
+
29
+ def left?
30
+ !right?
31
+ end
32
+
33
+ def right?
34
+ @right
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ pin 'head', to: 'head/index.js'
4
+
5
+ pin 'head/knob', to: 'head/knob.js'
6
+ pin 'head/wing', to: 'head/wing.js'
@@ -0,0 +1 @@
1
+ en:
data/lib/head/css.rb ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Head
4
+ # Simple helper to unify Strings and Arrays of CSS class names.
5
+ class Css
6
+ def self.call(*input)
7
+ Array(input).flatten.compact.join(' ')
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/engine'
4
+
5
+ module Head
6
+ # :nodoc:
7
+ class Engine < ::Rails::Engine
8
+ isolate_namespace Head
9
+
10
+ config.autoload_paths << root.join('lib')
11
+
12
+ initializer 'head.importmap', before: 'importmap' do |app|
13
+ app.config.importmap.paths << Engine.root.join('config/importmap.rb')
14
+ # Watch JS changes in development
15
+ app.config.importmap.cache_sweepers << Engine.root.join('app/assets/javascripts')
16
+ end
17
+
18
+ config.to_prepare do
19
+ # Our Head components are subclasses of `ViewComponent::Base`.
20
+ # When `ViewComponent::Base` is subclassed, two things happen:
21
+ #
22
+ # 1. Rails routes are included into the component
23
+ # 2. The ViewComponent configuration is accessed
24
+ #
25
+ # So we can only require our components, once Rails has booted
26
+ # AND the view_component gem has been fully initialized (configured).
27
+ #
28
+ # That's right here and now.
29
+ require_relative '../../app/components/head/application_component'
30
+
31
+ # Components
32
+ require_relative '../../app/components/head/wing'
33
+ require_relative '../../app/components/head/knob'
34
+ require_relative '../../app/components/head/theater'
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest/md5'
4
+
5
+ module Head
6
+ class Identicon
7
+ DARK_COLORS = %w[#39FF14 #FF3131 #1F51FF #FFE700 #F000FF #7FFF00 #CCFF00 #FF5E00 #00FFFF].freeze
8
+ LIGHT_COLORS = [
9
+ 'A799B7', # gray
10
+ 'E384FF', # rose
11
+ 'c300cE', # purple
12
+ 'F8DE22', # yellow
13
+ '9EDDFF', # light blue
14
+ 'FF004D', # red
15
+ 'F72798', # pink
16
+ 'FFAF00', # orange
17
+ '39FF14' # neon grass green
18
+ ].freeze
19
+ GRID_SIZE = 5
20
+ HALF_GRID = (GRID_SIZE / 2.0).round
21
+ DENSITY = 0.5
22
+
23
+ def initialize(input, theme: :light)
24
+ @seed = Digest::MD5.hexdigest(input).to_i(16)
25
+ @random = Random.new(@seed)
26
+ colors = theme == :light ? LIGHT_COLORS : DARK_COLORS
27
+ @color = colors[@seed % (LIGHT_COLORS.size - 1)]
28
+ end
29
+
30
+ def to_svg
31
+ svg = +%(<svg viewBox="0 0 5 5" xmlns="http://www.w3.org/2000/svg" fill="" shape-rendering="crispEdges">)
32
+ GRID_SIZE.times do |row|
33
+ HALF_GRID.times do |column|
34
+ add_pixel(column, row, svg) if @random.rand < DENSITY
35
+ end
36
+ end
37
+ svg << '</svg>'
38
+ svg.html_safe
39
+ end
40
+
41
+ private
42
+
43
+ def add_pixel(column, row, svg)
44
+ positions = [[column, row]]
45
+ mirrored_column = GRID_SIZE - 1 - column
46
+ positions << [mirrored_column, row] unless column == mirrored_column
47
+
48
+ positions.each do |current_column, current_row|
49
+ svg << %(<rect x="#{current_column}" y="#{current_row}" width="1" height="1" fill="##{@color}"></rect>)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Head
4
+ VERSION = '0.0.1'
5
+ end
data/lib/head.rb ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'calls'
4
+ require 'dry/initializer'
5
+ require 'countries'
6
+ require 'holidays'
7
+ require 'importmap-rails'
8
+
9
+ require 'head/version'
10
+ require 'head/engine' if defined?(Rails::Engine)
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: head
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - halo
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: actionview
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: calls
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: countries
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: dry-initializer
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: holidays
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: importmap-rails
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: view_component
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: With sidebars that work well on all screen widths.
111
+ email:
112
+ - github@posteo.org
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - CHANGELOG.md
118
+ - LICENSE.txt
119
+ - README.md
120
+ - app/assets/fonts/head/headicons.woff
121
+ - app/assets/fonts/icomoon.json
122
+ - app/assets/javascript/head/index.js
123
+ - app/assets/javascript/head/knob.js
124
+ - app/assets/javascript/head/wing.js
125
+ - app/assets/stylesheets/head/components/knob.sass
126
+ - app/assets/stylesheets/head/components/theater.sass
127
+ - app/assets/stylesheets/head/components/wing.sass
128
+ - app/assets/stylesheets/head/generics/flip.sass
129
+ - app/assets/stylesheets/head/generics/fonts.sass
130
+ - app/assets/stylesheets/head/index.sass
131
+ - app/assets/stylesheets/head/objects/headicon.sass
132
+ - app/assets/stylesheets/head/package.json
133
+ - app/assets/stylesheets/head/settings/_colors.sass
134
+ - app/assets/stylesheets/head/settings/_icons.scss
135
+ - app/assets/stylesheets/head/tools/_cloud.sass
136
+ - app/assets/stylesheets/head/tools/_link.sass
137
+ - app/assets/stylesheets/head/tools/_mandatory.sass
138
+ - app/assets/stylesheets/head/tools/_terminal.sass
139
+ - app/assets/stylesheets/head/tools/_theme.sass
140
+ - app/assets/stylesheets/head/utilities/smokescreen.sass
141
+ - app/components/head/application_component.rb
142
+ - app/components/head/knob.rb
143
+ - app/components/head/theater.rb
144
+ - app/components/head/wing.rb
145
+ - config/importmap.rb
146
+ - config/locales/head.en.yml
147
+ - lib/head.rb
148
+ - lib/head/css.rb
149
+ - lib/head/engine.rb
150
+ - lib/head/identicon.rb
151
+ - lib/head/version.rb
152
+ homepage: https://github.com/halo/head
153
+ licenses:
154
+ - MIT
155
+ metadata:
156
+ homepage_uri: https://github.com/halo/head
157
+ source_code_uri: https://github.com/halo/head
158
+ changelog_uri: https://github.com/halo/halo/blob/main/CHANGELOG.md
159
+ rubygems_mfa_required: 'true'
160
+ rdoc_options: []
161
+ require_paths:
162
+ - lib
163
+ required_ruby_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '3.4'
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ requirements: []
174
+ rubygems_version: 3.7.2
175
+ specification_version: 4
176
+ summary: A simple, opinionated navigation menu ViewComponent
177
+ test_files: []