recourse 4.4.4 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -0
  3. data/README.md +6 -1
  4. data/app/javascript/recourse/density_controller.js +17 -0
  5. data/app/javascript/recourse/scheme_controller.js +64 -16
  6. data/app/stylesheets/recourse/themes/bootstrap.css +11 -0
  7. data/app/stylesheets/recourse/themes/dawn.css +189 -0
  8. data/app/stylesheets/recourse/themes/dracula.css +189 -0
  9. data/app/stylesheets/recourse/themes/gruvbox.css +189 -0
  10. data/app/stylesheets/recourse/themes/monokai.css +190 -0
  11. data/app/stylesheets/recourse/themes/nord.css +189 -0
  12. data/app/stylesheets/recourse/themes/one_dark.css +189 -0
  13. data/app/stylesheets/recourse/themes/solarized.css +188 -0
  14. data/app/stylesheets/recourse/themes/tokyo_night.css +189 -0
  15. data/app/views/layouts/recourses.html.erb +105 -83
  16. data/app/views/recourses/_footer.html.erb +3 -2
  17. data/app/views/recourses/_scheme.html.erb +18 -4
  18. data/app/views/recourses/_sidebar.html.erb +18 -1
  19. data/config/locales/recourse.en.yml +7 -2
  20. data/lib/recourse/colors.rb +7 -1
  21. data/lib/recourse/controllers.rb +31 -4
  22. data/lib/recourse/densities.rb +7 -0
  23. data/lib/recourse/engine.rb +6 -2
  24. data/lib/recourse/helpers/bookmarks.rb +3 -1
  25. data/lib/recourse/helpers/densities.rb +17 -0
  26. data/lib/recourse/helpers/formats.rb +11 -5
  27. data/lib/recourse/helpers/navigation.rb +5 -3
  28. data/lib/recourse/helpers/themes.rb +36 -0
  29. data/lib/recourse/helpers.rb +4 -3
  30. data/lib/recourse/routes/nested.rb +4 -3
  31. data/lib/recourse/themes.rb +49 -0
  32. data/lib/recourse/version.rb +1 -1
  33. data/lib/recourse.rb +2 -1
  34. metadata +17 -5
  35. data/lib/recourse/helpers/schemes.rb +0 -14
  36. data/lib/recourse/schemes.rb +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9a2407f2f271c099137cb116f77873a8e498e106f32ebe70e6b4ad20c789a987
4
- data.tar.gz: bc9c0f80bf263983fc51f4af82eb17675564b9f17eccb794284fec10f7ee59eb
3
+ metadata.gz: 314d13d5c8a4e44ae907b432a9f8c37d3c4f3401cb12856127681077171c969f
4
+ data.tar.gz: c389ca81f7dbad23c1f185b4f50ec479f901c7dace6d57d94020be9b811f8c80
5
5
  SHA512:
6
- metadata.gz: b9f10651a71c2e65a15c8f6a1f5c7d33b5ff67b516e9a528b3220ffa9e273cb88423afdda64d6301a9a09ab97614a0c7dfa701f15d1ea4bb1bc11b2a2bcedf25
7
- data.tar.gz: fd3b958dfeddf61fc1d368435e6675d9103d102d367ada4309f89c0b135a3c5da26e3069a7ba4ceb998881e6cd9a04bdb2041f6004e1729124a54b5290d49a44
6
+ metadata.gz: 1b1492f1befaab5648685a8894d255a70a376c3dbd076218ebe68d0cc5c88345e6f1debcc7598745fd0ec8b68151967bd896730b0dc50b9c8fa282bcb5dfcdf0
7
+ data.tar.gz: 213e0a0eefc0137b315ac8bfb35861286b72958edd686798a820a5a8388b627bd0f5905be5aa682027f4333eccb50291a42361788781a66d9b226a8ff9f19f12
data/CHANGELOG.md CHANGED
@@ -5,6 +5,47 @@ All notable changes to this project will be documented in this file.
5
5
  For more information about changelogs, check [Keep a Changelog](http://keepachangelog.com) and
6
6
  [Vandamme](http://tech-angels.github.io/vandamme).
7
7
 
8
+ ## 4.6.0 - 2026-09-08
9
+
10
+ * [Fix] On a phone the `Displaying items` sentence is centered over the pagination links, which already were
11
+
12
+ * [Feature] A phone reads the chrome as icons, and a pair of arrows at the foot of the sidebar puts the words back
13
+
14
+ The sidebar is one centered row of icons at the very top of the page, above the trail;
15
+ a crumb is its icon, a tab its icon and its figure, the foot's controls their icons. The
16
+ words are there for a screen reader, and for anyone who taps the arrows: they write a
17
+ `recourse-density` cookie and the server draws every page with the words beside the icons
18
+ until the next tap. A column at 768px and wider is unchanged. The arrows on that control
19
+ are unicon 3.6.0's `:expand` and `:collapse`, which the gem now requires.
20
+ * [Feature] On a phone, a page under a record puts its search form and its buttons on rows below the trail
21
+
22
+ Two or three crumbs left the form the width of `Filter by na`. An index at the top of
23
+ the sidebar keeps its form beside its one crumb.
24
+
25
+ * [Feature] `Recourse.theme` draws every page in one of eight editor color schemes, and the sidebar's moon rotates through them
26
+
27
+ Back from recourse 4's development tree, where it was cut for the release: Dawn,
28
+ Dracula, Gruvbox, Monokai, Nord, One Dark, Solarized and Tokyo Night, each a
29
+ stylesheet served from the engine that repaints Bootstrap's ramps in both modes, with
30
+ Bootstrap's own palette named `:bootstrap` beside them. A click on the moon or the sun
31
+ moves the page to another palette and into the other mode, and the reader's choice is
32
+ kept in their browser. `Recourse.color` still names the primary family on top of any.
33
+
34
+ ## 4.5.0 - 2026-09-08
35
+
36
+ * [Fix] A row with a bookmark square is as tall as a row without one: the square no longer claims a button's minimum height
37
+
38
+ * [Feature] The arrow after a web address opens it in a new tab, while the words still open it in this one
39
+
40
+ * [Fix] A production rake task boots without Rails 8.2 warning that Action Controller and Active Record were loaded early
41
+
42
+ `recourses` defined each missing controller as the routes drew, which loaded the host's
43
+ `RecoursesController` and everything above it, and asked a nested name for its model the
44
+ same way. A boot that will not eager load — a `db:migrate` on release — draws its routes
45
+ before it is done, and Rails 8.2 logs a warning for every component loaded then. The
46
+ controller now waits for the app's first request or job, and the model is looked up as a
47
+ constant, without loading it.
48
+
8
49
  ## 4.4.4 - 2026-09-08
9
50
 
10
51
  * [Fix] On a phone the search form keeps its place beside the breadcrumb once a filter is picked
data/README.md CHANGED
@@ -124,13 +124,18 @@ Here are some bonus features the gem provides. Customize them by editing their v
124
124
  your `config/initializers/recourse.rb` file:
125
125
 
126
126
  - [`Recourse.color=`](https://rubydoc.info/gems/recourse/Recourse#color%3D-class_method):
127
- which Bootstrap family is primary on every page. Readers switch light and dark themselves.
127
+ which Bootstrap family is primary on every page.
128
+ - [`Recourse.theme=`](https://rubydoc.info/gems/recourse/Recourse#theme%3D-class_method):
129
+ which color scheme every page is drawn in, one of `Recourse::THEMES.keys` — eight from
130
+ code editors and Bootstrap's own. Readers rotate through them, light and dark, from the
131
+ moon at the foot of the sidebar; their choice stays in their browser.
128
132
  - [`Recourse.bookmarks=`](https://rubydoc.info/gems/recourse/Recourse#bookmarks%3D-class_method):
129
133
  a Proc answering the viewer's bookmark rows. Every table of a model with a `has_many`
130
134
  at that class then opens with a square to keep a row by, kept rows first.
131
135
 
132
136
  ```ruby
133
137
  Recourse.color = :orange
138
+ Recourse.theme = :nord
134
139
  Recourse.bookmarks = -> { Keepsake.where agent: Current.agent }
135
140
  ```
136
141
 
@@ -0,0 +1,17 @@
1
+ import { Controller } from '/recourse/stimulus.js'
2
+
3
+ // The arrows at the foot of the sidebar, drawn on a phone alone: a tap puts the words
4
+ // back beside every icon the chrome shows — the sidebar's entries, the crumbs, the tabs,
5
+ // the foot's own controls — and the next tap takes them away again. The choice goes to
6
+ // the server in a cookie, the way the zone does, so the next page is drawn with the
7
+ // words in rather than drawn without them and widened once a script runs.
8
+ export default class extends Controller {
9
+ static values = { storage: String }
10
+
11
+ toggle() {
12
+ const expanded = document.body.classList.toggle('recourse-expanded')
13
+ const density = expanded ? 'expanded' : 'compact'
14
+
15
+ document.cookie = `${this.storageValue}=${density}; path=/; max-age=31536000; samesite=lax`
16
+ }
17
+ }
@@ -1,36 +1,45 @@
1
1
  import { Controller } from '/recourse/stimulus.js'
2
2
 
3
- // The sidebar's one control over how a page looks. A click moves it into the mode it is
4
- // not in, which is what the icon promises: a moon while the page is light, a sun while
5
- // it is dark. The choice belongs to the reader rather than to the app, so it is kept in
6
- // their browser — the layout's own script is what puts it back before the first paint,
7
- // a controller connecting far too late for that.
3
+ // The sidebar's one control over how a page looks. A click moves it to another palette
4
+ // and into the mode it is not in, which is what the icon promises: a moon while the page
5
+ // is light, a sun while it is dark. The choice belongs to the reader rather than to the
6
+ // app, so it is kept in their browser — the layout's own script is what puts it back
7
+ // before the first paint, a controller connecting far too late for that.
8
8
  export default class extends Controller {
9
- static values = { storage: String }
9
+ static values = { themes: Array, path: String, storage: String }
10
10
 
11
- // Turbo merges the head on a visit, and the layout's script runs only on a full load.
12
- // The sidebar is drawn again on every visit, so connecting is the moment to say it again.
11
+ // Turbo merges the head on a visit, which puts the palette the server chose back over
12
+ // the one the reader picked, and the layout's script runs only on a full load. The
13
+ // sidebar is drawn again on every visit, so connecting is the moment to say it again.
13
14
  connect() {
14
15
  const stored = this.#stored()
15
16
 
16
- if (stored) { this.#apply(stored.mode) }
17
+ if (stored) { this.#apply(stored.theme, stored.mode) }
17
18
  }
18
19
 
19
- // The other mode, remembered.
20
+ // Another palette, in the other mode. Random rather than in order: the eight have no
21
+ // order that means anything. Excluding the one showing is what stops a click looking
22
+ // like it did nothing, which is the whole risk of picking at random.
20
23
  rotate() {
21
24
  const mode = this.#mode() === 'dark' ? 'light' : 'dark'
25
+ const others = this.themesValue.filter((theme) => theme !== this.#theme())
26
+ const theme = others[Math.floor(Math.random() * others.length)]
22
27
 
23
- this.#apply(mode)
24
- localStorage.setItem(this.storageValue, JSON.stringify({ mode }))
28
+ this.#apply(theme, mode)
29
+ localStorage.setItem(this.storageValue, JSON.stringify({ theme, mode }))
25
30
  }
26
31
 
27
- // The mode goes onto the element every `light-dark()` on the page is resolved against.
28
- // Checked first: the storage is the reader's own, but what comes back out of it is
29
- // still not something to write onto the page unread.
30
- #apply(mode) {
32
+ // The mode onto the element every `light-dark()` on the page is resolved against, and
33
+ // the palette onto the one link that serves one. A name is checked against the list
34
+ // the server sent: the storage is the reader's own, but what comes back out of it is
35
+ // still not something to put in a URL unread.
36
+ #apply(theme, mode) {
31
37
  if (mode === 'light' || mode === 'dark') {
32
38
  document.documentElement.dataset.bsTheme = mode
33
39
  }
40
+ if (!this.themesValue.includes(theme)) { return }
41
+
42
+ this.#link().href = `${this.pathValue}/${theme}.css`
34
43
  }
35
44
 
36
45
  // What the reader picked last, or nothing at all where they never have.
@@ -48,4 +57,43 @@ export default class extends Controller {
48
57
  return document.documentElement.dataset.bsTheme ||
49
58
  (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
50
59
  }
60
+
61
+ // Which palette is showing, read off the link rather than remembered, so a reader who
62
+ // cleared their storage still moves on from the one in front of them.
63
+ #theme() {
64
+ const link = this.#current()
65
+
66
+ return link ? link.href.split('/').pop().replace('.css', '') : null
67
+ }
68
+
69
+ // The palette the page is drawn in, which is the *last* of these links rather than the
70
+ // first. Turbo's head merge appends a stylesheet the new head has and the old one does
71
+ // not — it never replaces one — and the server names the same palette on every page,
72
+ // so a visit made after the reader chose leaves two links and the browser obeys the
73
+ // one at the end. Writing to the other would change nothing anybody can see.
74
+ #current() {
75
+ const links = document.querySelectorAll('link[data-recourse-theme]')
76
+
77
+ return links[links.length - 1]
78
+ }
79
+
80
+ // The one link a palette is served through: whichever is in force, with any the merge
81
+ // left behind it taken away, and a new one where a host named no palette at all and
82
+ // there is none in the head to find.
83
+ #link() {
84
+ const links = [...document.querySelectorAll('link[data-recourse-theme]')]
85
+ const link = links.pop() || this.#appended()
86
+ links.forEach((stale) => stale.remove())
87
+
88
+ return link
89
+ }
90
+
91
+ #appended() {
92
+ const link = document.createElement('link')
93
+ link.rel = 'stylesheet'
94
+ link.dataset.recourseTheme = ''
95
+ document.head.appendChild(link)
96
+
97
+ return link
98
+ }
51
99
  }
@@ -0,0 +1,11 @@
1
+ /* Bootstrap's own palette, and the only file here that declares nothing.
2
+
3
+ The eight beside it work by overriding upstream's `:root`, so having upstream's
4
+ palette back is a matter of dropping their block rather than of writing one — and
5
+ swapping this link in is how the sidebar's toggle drops it. `Recourse.theme = nil`
6
+ and `:bootstrap` are the same look by two routes: the first links no stylesheet at
7
+ all, the second links this one.
8
+
9
+ It is named so the toggle can reach it. A reader who rotates through nine palettes
10
+ and never finds the one the pages started in has been shown a door with no handle
11
+ on the inside. */
@@ -0,0 +1,189 @@
1
+ /* Rose Pine Dawn, Rose Pine — https://rosepinetheme.com/palette/
2
+ Dawn is the light page and Rose Pine's own base the dark one. pine
3
+ serves blue, foam cyan, rose pink, love red, iris purple, gold yellow,
4
+ orange and brown. green is derived: Rose Pine carries none.
5
+
6
+ Bootstrap builds a family by mixing one base toward `--bs-white` below
7
+ the 500 step and toward `--bs-black` above it, and inlines that base
8
+ into all thirteen — so a palette restates the thirteen rather than the
9
+ one. Upstream's order and upstream's shapes with only the base swapped,
10
+ so a later Bootstrap diffs against this file. */
11
+ :root {
12
+ /* The page, and the two colors every ramp below is mixed with.
13
+ `--bs-bg-body` reads `--bs-white` in light mode and `--bs-gray-975` in
14
+ dark, so repainting these two is what moves the page itself. */
15
+ --bs-white: #faf4ed;
16
+ --bs-black: #191724;
17
+
18
+ /* The neutrals, which every surface, border and text token Bootstrap ships
19
+ derives from. */
20
+ --bs-gray-025: color-mix(in lab, var(--bs-white) 94%, #9893a5);
21
+ --bs-gray-050: color-mix(in lab, var(--bs-white) 90%, #9893a5);
22
+ --bs-gray-100: color-mix(in lab, var(--bs-white) 80%, #9893a5);
23
+ --bs-gray-200: color-mix(in lab, var(--bs-white) 60%, #9893a5);
24
+ --bs-gray-300: color-mix(in lab, var(--bs-white) 40%, #9893a5);
25
+ --bs-gray-400: color-mix(in lab, var(--bs-white) 20%, #9893a5);
26
+ --bs-gray-500: #9893a5;
27
+ --bs-gray-600: color-mix(in lab, var(--bs-black) 16%, #9893a5);
28
+ --bs-gray-700: color-mix(in lab, var(--bs-black) 32%, #9893a5);
29
+ --bs-gray-800: color-mix(in lab, var(--bs-black) 48%, #9893a5);
30
+ --bs-gray-900: color-mix(in lab, var(--bs-black) 64%, #9893a5);
31
+ --bs-gray-950: color-mix(in lab, var(--bs-black) 76%, #9893a5);
32
+ --bs-gray-975: color-mix(in lab, var(--bs-black) 88%, #9893a5);
33
+
34
+ --bs-blue-025: color-mix(in lab, var(--bs-white) 94%, #286983);
35
+ --bs-blue-050: color-mix(in lab, var(--bs-white) 90%, #286983);
36
+ --bs-blue-100: color-mix(in lab, var(--bs-white) 80%, #286983);
37
+ --bs-blue-200: color-mix(in lab, var(--bs-white) 60%, #286983);
38
+ --bs-blue-300: color-mix(in lab, var(--bs-white) 40%, #286983);
39
+ --bs-blue-400: color-mix(in lab, var(--bs-white) 20%, #286983);
40
+ --bs-blue-500: #286983;
41
+ --bs-blue-600: color-mix(in lab, var(--bs-black) 16%, #286983);
42
+ --bs-blue-700: color-mix(in lab, var(--bs-black) 32%, #286983);
43
+ --bs-blue-800: color-mix(in lab, var(--bs-black) 48%, #286983);
44
+ --bs-blue-900: color-mix(in lab, var(--bs-black) 64%, #286983);
45
+ --bs-blue-950: color-mix(in lab, var(--bs-black) 76%, #286983);
46
+ --bs-blue-975: color-mix(in lab, var(--bs-black) 88%, #286983);
47
+
48
+ --bs-brown-025: color-mix(in lab, var(--bs-white) 94%, #ea9d34);
49
+ --bs-brown-050: color-mix(in lab, var(--bs-white) 90%, #ea9d34);
50
+ --bs-brown-100: color-mix(in lab, var(--bs-white) 80%, #ea9d34);
51
+ --bs-brown-200: color-mix(in lab, var(--bs-white) 60%, #ea9d34);
52
+ --bs-brown-300: color-mix(in lab, var(--bs-white) 40%, #ea9d34);
53
+ --bs-brown-400: color-mix(in lab, var(--bs-white) 20%, #ea9d34);
54
+ --bs-brown-500: #ea9d34;
55
+ --bs-brown-600: color-mix(in lab, var(--bs-black) 16%, #ea9d34);
56
+ --bs-brown-700: color-mix(in lab, var(--bs-black) 32%, #ea9d34);
57
+ --bs-brown-800: color-mix(in lab, var(--bs-black) 48%, #ea9d34);
58
+ --bs-brown-900: color-mix(in lab, var(--bs-black) 64%, #ea9d34);
59
+ --bs-brown-950: color-mix(in lab, var(--bs-black) 76%, #ea9d34);
60
+ --bs-brown-975: color-mix(in lab, var(--bs-black) 88%, #ea9d34);
61
+
62
+ --bs-cyan-025: color-mix(in lab, var(--bs-white) 94%, #56949f);
63
+ --bs-cyan-050: color-mix(in lab, var(--bs-white) 90%, #56949f);
64
+ --bs-cyan-100: color-mix(in lab, var(--bs-white) 80%, #56949f);
65
+ --bs-cyan-200: color-mix(in lab, var(--bs-white) 60%, #56949f);
66
+ --bs-cyan-300: color-mix(in lab, var(--bs-white) 40%, #56949f);
67
+ --bs-cyan-400: color-mix(in lab, var(--bs-white) 20%, #56949f);
68
+ --bs-cyan-500: #56949f;
69
+ --bs-cyan-600: color-mix(in lab, var(--bs-black) 16%, #56949f);
70
+ --bs-cyan-700: color-mix(in lab, var(--bs-black) 32%, #56949f);
71
+ --bs-cyan-800: color-mix(in lab, var(--bs-black) 48%, #56949f);
72
+ --bs-cyan-900: color-mix(in lab, var(--bs-black) 64%, #56949f);
73
+ --bs-cyan-950: color-mix(in lab, var(--bs-black) 76%, #56949f);
74
+ --bs-cyan-975: color-mix(in lab, var(--bs-black) 88%, #56949f);
75
+
76
+ --bs-green-025: color-mix(in lab, var(--bs-white) 94%, #4f8a63);
77
+ --bs-green-050: color-mix(in lab, var(--bs-white) 90%, #4f8a63);
78
+ --bs-green-100: color-mix(in lab, var(--bs-white) 80%, #4f8a63);
79
+ --bs-green-200: color-mix(in lab, var(--bs-white) 60%, #4f8a63);
80
+ --bs-green-300: color-mix(in lab, var(--bs-white) 40%, #4f8a63);
81
+ --bs-green-400: color-mix(in lab, var(--bs-white) 20%, #4f8a63);
82
+ --bs-green-500: #4f8a63;
83
+ --bs-green-600: color-mix(in lab, var(--bs-black) 16%, #4f8a63);
84
+ --bs-green-700: color-mix(in lab, var(--bs-black) 32%, #4f8a63);
85
+ --bs-green-800: color-mix(in lab, var(--bs-black) 48%, #4f8a63);
86
+ --bs-green-900: color-mix(in lab, var(--bs-black) 64%, #4f8a63);
87
+ --bs-green-950: color-mix(in lab, var(--bs-black) 76%, #4f8a63);
88
+ --bs-green-975: color-mix(in lab, var(--bs-black) 88%, #4f8a63);
89
+
90
+ --bs-orange-025: color-mix(in lab, var(--bs-white) 94%, #ea9d34);
91
+ --bs-orange-050: color-mix(in lab, var(--bs-white) 90%, #ea9d34);
92
+ --bs-orange-100: color-mix(in lab, var(--bs-white) 80%, #ea9d34);
93
+ --bs-orange-200: color-mix(in lab, var(--bs-white) 60%, #ea9d34);
94
+ --bs-orange-300: color-mix(in lab, var(--bs-white) 40%, #ea9d34);
95
+ --bs-orange-400: color-mix(in lab, var(--bs-white) 20%, #ea9d34);
96
+ --bs-orange-500: #ea9d34;
97
+ --bs-orange-600: color-mix(in lab, var(--bs-black) 16%, #ea9d34);
98
+ --bs-orange-700: color-mix(in lab, var(--bs-black) 32%, #ea9d34);
99
+ --bs-orange-800: color-mix(in lab, var(--bs-black) 48%, #ea9d34);
100
+ --bs-orange-900: color-mix(in lab, var(--bs-black) 64%, #ea9d34);
101
+ --bs-orange-950: color-mix(in lab, var(--bs-black) 76%, #ea9d34);
102
+ --bs-orange-975: color-mix(in lab, var(--bs-black) 88%, #ea9d34);
103
+
104
+ --bs-pink-025: color-mix(in lab, var(--bs-white) 94%, #d7827e);
105
+ --bs-pink-050: color-mix(in lab, var(--bs-white) 90%, #d7827e);
106
+ --bs-pink-100: color-mix(in lab, var(--bs-white) 80%, #d7827e);
107
+ --bs-pink-200: color-mix(in lab, var(--bs-white) 60%, #d7827e);
108
+ --bs-pink-300: color-mix(in lab, var(--bs-white) 40%, #d7827e);
109
+ --bs-pink-400: color-mix(in lab, var(--bs-white) 20%, #d7827e);
110
+ --bs-pink-500: #d7827e;
111
+ --bs-pink-600: color-mix(in lab, var(--bs-black) 16%, #d7827e);
112
+ --bs-pink-700: color-mix(in lab, var(--bs-black) 32%, #d7827e);
113
+ --bs-pink-800: color-mix(in lab, var(--bs-black) 48%, #d7827e);
114
+ --bs-pink-900: color-mix(in lab, var(--bs-black) 64%, #d7827e);
115
+ --bs-pink-950: color-mix(in lab, var(--bs-black) 76%, #d7827e);
116
+ --bs-pink-975: color-mix(in lab, var(--bs-black) 88%, #d7827e);
117
+
118
+ --bs-purple-025: color-mix(in lab, var(--bs-white) 94%, #907aa9);
119
+ --bs-purple-050: color-mix(in lab, var(--bs-white) 90%, #907aa9);
120
+ --bs-purple-100: color-mix(in lab, var(--bs-white) 80%, #907aa9);
121
+ --bs-purple-200: color-mix(in lab, var(--bs-white) 60%, #907aa9);
122
+ --bs-purple-300: color-mix(in lab, var(--bs-white) 40%, #907aa9);
123
+ --bs-purple-400: color-mix(in lab, var(--bs-white) 20%, #907aa9);
124
+ --bs-purple-500: #907aa9;
125
+ --bs-purple-600: color-mix(in lab, var(--bs-black) 16%, #907aa9);
126
+ --bs-purple-700: color-mix(in lab, var(--bs-black) 32%, #907aa9);
127
+ --bs-purple-800: color-mix(in lab, var(--bs-black) 48%, #907aa9);
128
+ --bs-purple-900: color-mix(in lab, var(--bs-black) 64%, #907aa9);
129
+ --bs-purple-950: color-mix(in lab, var(--bs-black) 76%, #907aa9);
130
+ --bs-purple-975: color-mix(in lab, var(--bs-black) 88%, #907aa9);
131
+
132
+ --bs-red-025: color-mix(in lab, var(--bs-white) 94%, #b4637a);
133
+ --bs-red-050: color-mix(in lab, var(--bs-white) 90%, #b4637a);
134
+ --bs-red-100: color-mix(in lab, var(--bs-white) 80%, #b4637a);
135
+ --bs-red-200: color-mix(in lab, var(--bs-white) 60%, #b4637a);
136
+ --bs-red-300: color-mix(in lab, var(--bs-white) 40%, #b4637a);
137
+ --bs-red-400: color-mix(in lab, var(--bs-white) 20%, #b4637a);
138
+ --bs-red-500: #b4637a;
139
+ --bs-red-600: color-mix(in lab, var(--bs-black) 16%, #b4637a);
140
+ --bs-red-700: color-mix(in lab, var(--bs-black) 32%, #b4637a);
141
+ --bs-red-800: color-mix(in lab, var(--bs-black) 48%, #b4637a);
142
+ --bs-red-900: color-mix(in lab, var(--bs-black) 64%, #b4637a);
143
+ --bs-red-950: color-mix(in lab, var(--bs-black) 76%, #b4637a);
144
+ --bs-red-975: color-mix(in lab, var(--bs-black) 88%, #b4637a);
145
+
146
+ --bs-yellow-025: color-mix(in lab, var(--bs-white) 94%, #ea9d34);
147
+ --bs-yellow-050: color-mix(in lab, var(--bs-white) 90%, #ea9d34);
148
+ --bs-yellow-100: color-mix(in lab, var(--bs-white) 80%, #ea9d34);
149
+ --bs-yellow-200: color-mix(in lab, var(--bs-white) 60%, #ea9d34);
150
+ --bs-yellow-300: color-mix(in lab, var(--bs-white) 40%, #ea9d34);
151
+ --bs-yellow-400: color-mix(in lab, var(--bs-white) 20%, #ea9d34);
152
+ --bs-yellow-500: #ea9d34;
153
+ --bs-yellow-600: color-mix(in lab, var(--bs-black) 16%, #ea9d34);
154
+ --bs-yellow-700: color-mix(in lab, var(--bs-black) 32%, #ea9d34);
155
+ --bs-yellow-800: color-mix(in lab, var(--bs-black) 48%, #ea9d34);
156
+ --bs-yellow-900: color-mix(in lab, var(--bs-black) 64%, #ea9d34);
157
+ --bs-yellow-950: color-mix(in lab, var(--bs-black) 76%, #ea9d34);
158
+ --bs-yellow-975: color-mix(in lab, var(--bs-black) 88%, #ea9d34);
159
+
160
+ /* Text, stated rather than left to the ramp. Bootstrap pairs a low step as
161
+ a light surface with that same step as dark-mode text, which a palette
162
+ built for its own contrast does not agree with. */
163
+ --bs-fg-1: light-dark(#575279, #e0def4);
164
+ --bs-fg-body: light-dark(#575279, #c8c3dd);
165
+ --bs-fg-2: light-dark(#797593, #908caa);
166
+
167
+ /* The label a solid fill carries, for the four families Bootstrap has
168
+ spent on a meaning and this palette has repainted. Upstream's own shape:
169
+ it gives warning and info a dark label for the same reason. */
170
+ --bs-danger-contrast: var(--bs-white);
171
+ --bs-success-contrast: var(--bs-white);
172
+ --bs-warning-contrast: var(--bs-gray-975);
173
+ --bs-info-contrast: var(--bs-gray-975);
174
+
175
+ /* The accent this palette leads with, as the primary color: the same nine
176
+ `_color.html.erb` writes, from blue and the label that reads on it. Here
177
+ rather than only in the page because a reader may swap this file for
178
+ another one, and the primary has to travel with it. A host naming its
179
+ own color still wins, its block coming after this file. */
180
+ --bs-primary-base: var(--bs-blue-500);
181
+ --bs-primary-fg: light-dark(var(--bs-blue-600), var(--bs-blue-400));
182
+ --bs-primary-fg-emphasis: light-dark(var(--bs-blue-800), var(--bs-blue-200));
183
+ --bs-primary-bg: var(--bs-blue-500);
184
+ --bs-primary-bg-subtle: light-dark(var(--bs-blue-100), var(--bs-blue-900));
185
+ --bs-primary-bg-muted: light-dark(var(--bs-blue-200), var(--bs-blue-800));
186
+ --bs-primary-border: light-dark(var(--bs-blue-300), var(--bs-blue-600));
187
+ --bs-primary-focus-ring: light-dark(color-mix(in oklch, var(--bs-blue-500) 50%, var(--bs-bg-body)), color-mix(in oklch, var(--bs-blue-500) 75%, var(--bs-bg-body)));
188
+ --bs-primary-contrast: var(--bs-white);
189
+ }
@@ -0,0 +1,189 @@
1
+ /* Dracula, Zeno Rocha — https://draculatheme.com/
2
+ Dracula's own foreground is the light page and its background the dark
3
+ one. its cyan serves blue and cyan, its orange serves brown. Dracula
4
+ leads with purple: it publishes no blue.
5
+
6
+ Bootstrap builds a family by mixing one base toward `--bs-white` below
7
+ the 500 step and toward `--bs-black` above it, and inlines that base
8
+ into all thirteen — so a palette restates the thirteen rather than the
9
+ one. Upstream's order and upstream's shapes with only the base swapped,
10
+ so a later Bootstrap diffs against this file. */
11
+ :root {
12
+ /* The page, and the two colors every ramp below is mixed with.
13
+ `--bs-bg-body` reads `--bs-white` in light mode and `--bs-gray-975` in
14
+ dark, so repainting these two is what moves the page itself. */
15
+ --bs-white: #f8f8f2;
16
+ --bs-black: #282a36;
17
+
18
+ /* The neutrals, which every surface, border and text token Bootstrap ships
19
+ derives from. */
20
+ --bs-gray-025: color-mix(in lab, var(--bs-white) 94%, #6272a4);
21
+ --bs-gray-050: color-mix(in lab, var(--bs-white) 90%, #6272a4);
22
+ --bs-gray-100: color-mix(in lab, var(--bs-white) 80%, #6272a4);
23
+ --bs-gray-200: color-mix(in lab, var(--bs-white) 60%, #6272a4);
24
+ --bs-gray-300: color-mix(in lab, var(--bs-white) 40%, #6272a4);
25
+ --bs-gray-400: color-mix(in lab, var(--bs-white) 20%, #6272a4);
26
+ --bs-gray-500: #6272a4;
27
+ --bs-gray-600: color-mix(in lab, var(--bs-black) 16%, #6272a4);
28
+ --bs-gray-700: color-mix(in lab, var(--bs-black) 32%, #6272a4);
29
+ --bs-gray-800: color-mix(in lab, var(--bs-black) 48%, #6272a4);
30
+ --bs-gray-900: color-mix(in lab, var(--bs-black) 64%, #6272a4);
31
+ --bs-gray-950: color-mix(in lab, var(--bs-black) 76%, #6272a4);
32
+ --bs-gray-975: color-mix(in lab, var(--bs-black) 88%, #6272a4);
33
+
34
+ --bs-blue-025: color-mix(in lab, var(--bs-white) 94%, #8be9fd);
35
+ --bs-blue-050: color-mix(in lab, var(--bs-white) 90%, #8be9fd);
36
+ --bs-blue-100: color-mix(in lab, var(--bs-white) 80%, #8be9fd);
37
+ --bs-blue-200: color-mix(in lab, var(--bs-white) 60%, #8be9fd);
38
+ --bs-blue-300: color-mix(in lab, var(--bs-white) 40%, #8be9fd);
39
+ --bs-blue-400: color-mix(in lab, var(--bs-white) 20%, #8be9fd);
40
+ --bs-blue-500: #8be9fd;
41
+ --bs-blue-600: color-mix(in lab, var(--bs-black) 16%, #8be9fd);
42
+ --bs-blue-700: color-mix(in lab, var(--bs-black) 32%, #8be9fd);
43
+ --bs-blue-800: color-mix(in lab, var(--bs-black) 48%, #8be9fd);
44
+ --bs-blue-900: color-mix(in lab, var(--bs-black) 64%, #8be9fd);
45
+ --bs-blue-950: color-mix(in lab, var(--bs-black) 76%, #8be9fd);
46
+ --bs-blue-975: color-mix(in lab, var(--bs-black) 88%, #8be9fd);
47
+
48
+ --bs-brown-025: color-mix(in lab, var(--bs-white) 94%, #ffb86c);
49
+ --bs-brown-050: color-mix(in lab, var(--bs-white) 90%, #ffb86c);
50
+ --bs-brown-100: color-mix(in lab, var(--bs-white) 80%, #ffb86c);
51
+ --bs-brown-200: color-mix(in lab, var(--bs-white) 60%, #ffb86c);
52
+ --bs-brown-300: color-mix(in lab, var(--bs-white) 40%, #ffb86c);
53
+ --bs-brown-400: color-mix(in lab, var(--bs-white) 20%, #ffb86c);
54
+ --bs-brown-500: #ffb86c;
55
+ --bs-brown-600: color-mix(in lab, var(--bs-black) 16%, #ffb86c);
56
+ --bs-brown-700: color-mix(in lab, var(--bs-black) 32%, #ffb86c);
57
+ --bs-brown-800: color-mix(in lab, var(--bs-black) 48%, #ffb86c);
58
+ --bs-brown-900: color-mix(in lab, var(--bs-black) 64%, #ffb86c);
59
+ --bs-brown-950: color-mix(in lab, var(--bs-black) 76%, #ffb86c);
60
+ --bs-brown-975: color-mix(in lab, var(--bs-black) 88%, #ffb86c);
61
+
62
+ --bs-cyan-025: color-mix(in lab, var(--bs-white) 94%, #8be9fd);
63
+ --bs-cyan-050: color-mix(in lab, var(--bs-white) 90%, #8be9fd);
64
+ --bs-cyan-100: color-mix(in lab, var(--bs-white) 80%, #8be9fd);
65
+ --bs-cyan-200: color-mix(in lab, var(--bs-white) 60%, #8be9fd);
66
+ --bs-cyan-300: color-mix(in lab, var(--bs-white) 40%, #8be9fd);
67
+ --bs-cyan-400: color-mix(in lab, var(--bs-white) 20%, #8be9fd);
68
+ --bs-cyan-500: #8be9fd;
69
+ --bs-cyan-600: color-mix(in lab, var(--bs-black) 16%, #8be9fd);
70
+ --bs-cyan-700: color-mix(in lab, var(--bs-black) 32%, #8be9fd);
71
+ --bs-cyan-800: color-mix(in lab, var(--bs-black) 48%, #8be9fd);
72
+ --bs-cyan-900: color-mix(in lab, var(--bs-black) 64%, #8be9fd);
73
+ --bs-cyan-950: color-mix(in lab, var(--bs-black) 76%, #8be9fd);
74
+ --bs-cyan-975: color-mix(in lab, var(--bs-black) 88%, #8be9fd);
75
+
76
+ --bs-green-025: color-mix(in lab, var(--bs-white) 94%, #50fa7b);
77
+ --bs-green-050: color-mix(in lab, var(--bs-white) 90%, #50fa7b);
78
+ --bs-green-100: color-mix(in lab, var(--bs-white) 80%, #50fa7b);
79
+ --bs-green-200: color-mix(in lab, var(--bs-white) 60%, #50fa7b);
80
+ --bs-green-300: color-mix(in lab, var(--bs-white) 40%, #50fa7b);
81
+ --bs-green-400: color-mix(in lab, var(--bs-white) 20%, #50fa7b);
82
+ --bs-green-500: #50fa7b;
83
+ --bs-green-600: color-mix(in lab, var(--bs-black) 16%, #50fa7b);
84
+ --bs-green-700: color-mix(in lab, var(--bs-black) 32%, #50fa7b);
85
+ --bs-green-800: color-mix(in lab, var(--bs-black) 48%, #50fa7b);
86
+ --bs-green-900: color-mix(in lab, var(--bs-black) 64%, #50fa7b);
87
+ --bs-green-950: color-mix(in lab, var(--bs-black) 76%, #50fa7b);
88
+ --bs-green-975: color-mix(in lab, var(--bs-black) 88%, #50fa7b);
89
+
90
+ --bs-orange-025: color-mix(in lab, var(--bs-white) 94%, #ffb86c);
91
+ --bs-orange-050: color-mix(in lab, var(--bs-white) 90%, #ffb86c);
92
+ --bs-orange-100: color-mix(in lab, var(--bs-white) 80%, #ffb86c);
93
+ --bs-orange-200: color-mix(in lab, var(--bs-white) 60%, #ffb86c);
94
+ --bs-orange-300: color-mix(in lab, var(--bs-white) 40%, #ffb86c);
95
+ --bs-orange-400: color-mix(in lab, var(--bs-white) 20%, #ffb86c);
96
+ --bs-orange-500: #ffb86c;
97
+ --bs-orange-600: color-mix(in lab, var(--bs-black) 16%, #ffb86c);
98
+ --bs-orange-700: color-mix(in lab, var(--bs-black) 32%, #ffb86c);
99
+ --bs-orange-800: color-mix(in lab, var(--bs-black) 48%, #ffb86c);
100
+ --bs-orange-900: color-mix(in lab, var(--bs-black) 64%, #ffb86c);
101
+ --bs-orange-950: color-mix(in lab, var(--bs-black) 76%, #ffb86c);
102
+ --bs-orange-975: color-mix(in lab, var(--bs-black) 88%, #ffb86c);
103
+
104
+ --bs-pink-025: color-mix(in lab, var(--bs-white) 94%, #ff79c6);
105
+ --bs-pink-050: color-mix(in lab, var(--bs-white) 90%, #ff79c6);
106
+ --bs-pink-100: color-mix(in lab, var(--bs-white) 80%, #ff79c6);
107
+ --bs-pink-200: color-mix(in lab, var(--bs-white) 60%, #ff79c6);
108
+ --bs-pink-300: color-mix(in lab, var(--bs-white) 40%, #ff79c6);
109
+ --bs-pink-400: color-mix(in lab, var(--bs-white) 20%, #ff79c6);
110
+ --bs-pink-500: #ff79c6;
111
+ --bs-pink-600: color-mix(in lab, var(--bs-black) 16%, #ff79c6);
112
+ --bs-pink-700: color-mix(in lab, var(--bs-black) 32%, #ff79c6);
113
+ --bs-pink-800: color-mix(in lab, var(--bs-black) 48%, #ff79c6);
114
+ --bs-pink-900: color-mix(in lab, var(--bs-black) 64%, #ff79c6);
115
+ --bs-pink-950: color-mix(in lab, var(--bs-black) 76%, #ff79c6);
116
+ --bs-pink-975: color-mix(in lab, var(--bs-black) 88%, #ff79c6);
117
+
118
+ --bs-purple-025: color-mix(in lab, var(--bs-white) 94%, #bd93f9);
119
+ --bs-purple-050: color-mix(in lab, var(--bs-white) 90%, #bd93f9);
120
+ --bs-purple-100: color-mix(in lab, var(--bs-white) 80%, #bd93f9);
121
+ --bs-purple-200: color-mix(in lab, var(--bs-white) 60%, #bd93f9);
122
+ --bs-purple-300: color-mix(in lab, var(--bs-white) 40%, #bd93f9);
123
+ --bs-purple-400: color-mix(in lab, var(--bs-white) 20%, #bd93f9);
124
+ --bs-purple-500: #bd93f9;
125
+ --bs-purple-600: color-mix(in lab, var(--bs-black) 16%, #bd93f9);
126
+ --bs-purple-700: color-mix(in lab, var(--bs-black) 32%, #bd93f9);
127
+ --bs-purple-800: color-mix(in lab, var(--bs-black) 48%, #bd93f9);
128
+ --bs-purple-900: color-mix(in lab, var(--bs-black) 64%, #bd93f9);
129
+ --bs-purple-950: color-mix(in lab, var(--bs-black) 76%, #bd93f9);
130
+ --bs-purple-975: color-mix(in lab, var(--bs-black) 88%, #bd93f9);
131
+
132
+ --bs-red-025: color-mix(in lab, var(--bs-white) 94%, #ff5555);
133
+ --bs-red-050: color-mix(in lab, var(--bs-white) 90%, #ff5555);
134
+ --bs-red-100: color-mix(in lab, var(--bs-white) 80%, #ff5555);
135
+ --bs-red-200: color-mix(in lab, var(--bs-white) 60%, #ff5555);
136
+ --bs-red-300: color-mix(in lab, var(--bs-white) 40%, #ff5555);
137
+ --bs-red-400: color-mix(in lab, var(--bs-white) 20%, #ff5555);
138
+ --bs-red-500: #ff5555;
139
+ --bs-red-600: color-mix(in lab, var(--bs-black) 16%, #ff5555);
140
+ --bs-red-700: color-mix(in lab, var(--bs-black) 32%, #ff5555);
141
+ --bs-red-800: color-mix(in lab, var(--bs-black) 48%, #ff5555);
142
+ --bs-red-900: color-mix(in lab, var(--bs-black) 64%, #ff5555);
143
+ --bs-red-950: color-mix(in lab, var(--bs-black) 76%, #ff5555);
144
+ --bs-red-975: color-mix(in lab, var(--bs-black) 88%, #ff5555);
145
+
146
+ --bs-yellow-025: color-mix(in lab, var(--bs-white) 94%, #f1fa8c);
147
+ --bs-yellow-050: color-mix(in lab, var(--bs-white) 90%, #f1fa8c);
148
+ --bs-yellow-100: color-mix(in lab, var(--bs-white) 80%, #f1fa8c);
149
+ --bs-yellow-200: color-mix(in lab, var(--bs-white) 60%, #f1fa8c);
150
+ --bs-yellow-300: color-mix(in lab, var(--bs-white) 40%, #f1fa8c);
151
+ --bs-yellow-400: color-mix(in lab, var(--bs-white) 20%, #f1fa8c);
152
+ --bs-yellow-500: #f1fa8c;
153
+ --bs-yellow-600: color-mix(in lab, var(--bs-black) 16%, #f1fa8c);
154
+ --bs-yellow-700: color-mix(in lab, var(--bs-black) 32%, #f1fa8c);
155
+ --bs-yellow-800: color-mix(in lab, var(--bs-black) 48%, #f1fa8c);
156
+ --bs-yellow-900: color-mix(in lab, var(--bs-black) 64%, #f1fa8c);
157
+ --bs-yellow-950: color-mix(in lab, var(--bs-black) 76%, #f1fa8c);
158
+ --bs-yellow-975: color-mix(in lab, var(--bs-black) 88%, #f1fa8c);
159
+
160
+ /* Text, stated rather than left to the ramp. Bootstrap pairs a low step as
161
+ a light surface with that same step as dark-mode text, which a palette
162
+ built for its own contrast does not agree with. */
163
+ --bs-fg-1: light-dark(#282a36, #ffffff);
164
+ --bs-fg-body: light-dark(#21222c, #f8f8f2);
165
+ --bs-fg-2: light-dark(#4a5480, #bcc2de);
166
+
167
+ /* The label a solid fill carries, for the four families Bootstrap has
168
+ spent on a meaning and this palette has repainted. Upstream's own shape:
169
+ it gives warning and info a dark label for the same reason. */
170
+ --bs-danger-contrast: var(--bs-gray-975);
171
+ --bs-success-contrast: var(--bs-gray-975);
172
+ --bs-warning-contrast: var(--bs-gray-975);
173
+ --bs-info-contrast: var(--bs-gray-975);
174
+
175
+ /* The accent this palette leads with, as the primary color: the same nine
176
+ `_color.html.erb` writes, from purple and the label that reads on it.
177
+ Here rather than only in the page because a reader may swap this file
178
+ for another one, and the primary has to travel with it. A host naming
179
+ its own color still wins, its block coming after this file. */
180
+ --bs-primary-base: var(--bs-purple-500);
181
+ --bs-primary-fg: light-dark(var(--bs-purple-600), var(--bs-purple-400));
182
+ --bs-primary-fg-emphasis: light-dark(var(--bs-purple-800), var(--bs-purple-200));
183
+ --bs-primary-bg: var(--bs-purple-500);
184
+ --bs-primary-bg-subtle: light-dark(var(--bs-purple-100), var(--bs-purple-900));
185
+ --bs-primary-bg-muted: light-dark(var(--bs-purple-200), var(--bs-purple-800));
186
+ --bs-primary-border: light-dark(var(--bs-purple-300), var(--bs-purple-600));
187
+ --bs-primary-focus-ring: light-dark(color-mix(in oklch, var(--bs-purple-500) 50%, var(--bs-bg-body)), color-mix(in oklch, var(--bs-purple-500) 75%, var(--bs-bg-body)));
188
+ --bs-primary-contrast: var(--bs-gray-975);
189
+ }