keystone_ui-colors 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a6fe78366470d37cb8598bfa4181561c42eca52a409dd7648246c397584daad9
4
- data.tar.gz: dbbcf12bbc4fd745c85cf35a94d0608cabfa9565e7a92e87eb22a5a5faa17f01
3
+ metadata.gz: 56e2aecbc1dd2315bbc0add8ae80c083a23e42aa0e59fd8b05828d9cd4f93860
4
+ data.tar.gz: 19f53163686417b9b497fa0ffb27bc69946d3b46a84fc0efb6961561a13b2706
5
5
  SHA512:
6
- metadata.gz: 9764dcad37f39f954cc377abf737bad185b2ee9e1e0ce301982bd6a52d286a0069ca8513b5afb918df2411475b40040646c5e605a6c384edf8b88e3f3c5c4d9b
7
- data.tar.gz: 60f59e26247a9580e2422d873f0e8cb4d4daa2b9aecd5dda755972b76f7dbfb46be9cf07bed75954fb2ee591a0699ac94285e65ebf445611a8f8443e84ada4a9
6
+ metadata.gz: d85fa52f7c4552d4196bcf36d4bf517358f89adeb55bdcdd5fa8491629782a64945e0e15124eef204b9297ed3b2149831aeedf29f68586803a34c91bb49644c7
7
+ data.tar.gz: c4bcbef319b118827f8812c9c230761179817fd258582fa21356f1a611f832d89b3793a10f3105d748f805b60280856f683c54fdb0b0867fade78b799a80a2be
data/README.md CHANGED
@@ -82,7 +82,9 @@ KeystoneUi::Colors.configure do |config|
82
82
  config.default_template = :ocean # Fallback theme
83
83
  config.default_accent = "blue" # Fallback accent color
84
84
  config.default_surface = "zinc" # Fallback surface color
85
- config.default_mode = "light" # Fallback theme mode: "light", "dark" or "system"
85
+ config.default_mode = "light" # Fallback theme mode: "light", "dark", "system" or "custom"
86
+ config.default_background = "#ffffff" # Custom mode background when nothing else sets one
87
+ config.default_text = "#18181b" # Custom mode text colour when nothing else sets one
86
88
  config.layout = "application" # Layout for settings page
87
89
  end
88
90
  ```
@@ -91,8 +93,8 @@ All values shown are defaults and can be omitted.
91
93
 
92
94
  ## Light and Dark Mode
93
95
 
94
- Users choose Light, Dark or System on the settings page, and the choice is saved
95
- with their palette. keystone_ui renders each page in, strongest first:
96
+ Users choose Light, Dark, System or Custom on the settings page, and the choice is
97
+ saved with their palette. keystone_ui renders each page in, strongest first:
96
98
 
97
99
  1. The choice made with keystone_ui's `ui_theme_toggle` in this browser.
98
100
  2. The mode the signed-in user saved.
@@ -102,16 +104,44 @@ Saving a mode on the settings page clears the toggle's choice in that browser, s
102
104
  saved mode takes effect. Pages are marked through keystone_ui's
103
105
  `keystone_theme_attributes` helper on the layout's `html` tag.
104
106
 
107
+ ## Custom Mode
108
+
109
+ A page in Custom mode is drawn from a background colour and a text colour, which
110
+ the gem writes as `--color-custom-background` and `--color-custom-text` with the
111
+ rest of the palette. keystone_ui-styles draws white in the background and every
112
+ gray and zinc shade as a blend of the text into it.
113
+
114
+ - A preset theme supplies both colours.
115
+ - With custom colours, the surface colour the user picks is the background and
116
+ the Text Color picker sets the text.
117
+ - Anything not set falls back to `config.default_background` and
118
+ `config.default_text`.
119
+
120
+ ## Keeping a Page on the Host's Colours
121
+
122
+ A controller that declares `keystone_host_colors` renders its pages in the host's
123
+ configured accent, surface, custom colours and mode, whatever the signed-in user
124
+ saved. It takes the same `only:` and `except:` options as `before_action`.
125
+
126
+ ```ruby
127
+ class HomeController < ApplicationController
128
+ keystone_host_colors
129
+ end
130
+ ```
131
+
132
+ A choice made with keystone_ui's `ui_theme_toggle` in that browser still sets
133
+ light or dark on these pages.
134
+
105
135
  ## Preset Themes
106
136
 
107
- | Name | Accent | Surface | Description |
108
- |----------|---------|---------|--------------------------------|
109
- | Default | *(configured)* | *(configured)* | Uses config defaults |
110
- | Ocean | blue | slate | Cool blues with slate undertones |
111
- | Forest | emerald | stone | Natural greens with warm stone |
112
- | Twilight | violet | zinc | Deep violet with clean zinc |
113
- | Coral | rose | neutral | Warm rose with neutral balance |
114
- | Arctic | cyan | gray | Bright cyan with crisp gray |
137
+ | Name | Accent | Surface | Custom background | Custom text | Description |
138
+ |----------|---------|---------|-------------------|-------------|--------------------------------|
139
+ | Default | *(configured)* | *(configured)* | *(configured)* | *(configured)* | Uses config defaults |
140
+ | Ocean | blue | slate | `#e0f2fe` | `#0c4a6e` | Cool blues with slate undertones |
141
+ | Forest | emerald | stone | `#ecfdf5` | `#064e3b` | Natural greens with warm stone |
142
+ | Twilight | violet | zinc | `#1e1b4b` | `#ede9fe` | Deep violet with clean zinc |
143
+ | Coral | rose | neutral | `#fff1f2` | `#4c0519` | Warm rose with neutral balance |
144
+ | Arctic | cyan | gray | `#ecfeff` | `#164e63` | Bright cyan with crisp gray |
115
145
 
116
146
  ## Available Colors
117
147
 
@@ -187,7 +217,7 @@ Accepts named colors (`"blue"`) or hex values (`"#3b82f6"`) for `accent` and `su
187
217
 
188
218
  ## Updating
189
219
 
190
- Run the update generator to get the latest Stimulus controller and any new migrations, then migrate:
220
+ Run the update generator to get the latest Stimulus controller and any new migrations (such as the text colour column), then migrate:
191
221
 
192
222
  ```bash
193
223
  bin/rails generate keystone_ui:colors:update
@@ -11,23 +11,26 @@ module KeystoneUi
11
11
  helper_method :keystone_palette_css, :keystone_theme_mode if respond_to?(:helper_method)
12
12
  end
13
13
 
14
+ class_methods do
15
+ def keystone_host_colors(**options)
16
+ before_action :apply_host_palette, **options
17
+ end
18
+ end
19
+
14
20
  def set_current_palette
15
21
  owner = send(KeystoneUi::Colors.configuration.current_owner_method)
16
22
 
17
- unless owner
18
- @keystone_theme_mode = KeystoneUi::Colors.configuration.default_mode
19
- build_palette_css(
20
- KeystoneUi::Colors.configuration.default_accent,
21
- KeystoneUi::Colors.configuration.default_surface
22
- )
23
- return
24
- end
23
+ return apply_host_palette unless owner
25
24
 
26
25
  cached = session[:keystone_ui_colors_palette]
27
26
 
28
27
  if cached && !stale_cache?(owner, cached)
29
28
  @keystone_theme_mode = cached[:mode] || KeystoneUi::Colors.configuration.default_mode
30
- build_palette_css(cached[:accent], cached[:surface])
29
+ build_palette_css(
30
+ cached[:accent],
31
+ cached[:surface],
32
+ KeystoneUi::Colors::CustomColours.new(template_name: cached[:template_name], surface: cached[:surface], text: cached[:text])
33
+ )
31
34
  return
32
35
  end
33
36
 
@@ -36,18 +39,30 @@ module KeystoneUi
36
39
  accent = preference&.accent || KeystoneUi::Colors.configuration.default_accent
37
40
  surface = preference&.surface || KeystoneUi::Colors.configuration.default_surface
38
41
 
39
- build_palette_css(accent, surface)
42
+ custom = KeystoneUi::Colors::CustomColours.new(template_name: preference&.template_name, surface: surface, text: preference&.text)
43
+ build_palette_css(accent, surface, custom)
40
44
 
41
45
  if preference
42
46
  session[:keystone_ui_colors_palette] = {
43
47
  accent: preference.accent,
44
48
  surface: preference.surface,
45
49
  mode: preference.mode,
50
+ template_name: preference.template_name,
51
+ text: preference.text,
46
52
  updated_at: preference.updated_at.to_i
47
53
  }
48
54
  end
49
55
  end
50
56
 
57
+ def apply_host_palette
58
+ @keystone_theme_mode = KeystoneUi::Colors.configuration.default_mode
59
+ build_palette_css(
60
+ KeystoneUi::Colors.configuration.default_accent,
61
+ KeystoneUi::Colors.configuration.default_surface,
62
+ KeystoneUi::Colors::CustomColours.new(template_name: nil, surface: nil, text: nil)
63
+ )
64
+ end
65
+
51
66
  def keystone_palette_css
52
67
  @keystone_palette_css
53
68
  end
@@ -58,13 +73,15 @@ module KeystoneUi
58
73
 
59
74
  private
60
75
 
61
- def build_palette_css(accent, surface)
76
+ def build_palette_css(accent, surface, custom = nil)
62
77
  accent_shades = resolve_shades(accent, :accent)
63
78
  surface_shades = resolve_shades(surface, :surface)
64
79
 
65
80
  lines = []
66
81
  accent_shades.each { |shade, hex| lines << " --color-accent-#{shade}: #{hex};" }
67
82
  surface_shades.each { |shade, hex| lines << " --color-surface-#{shade}: #{hex};" }
83
+ lines << " --color-custom-background: #{custom.background};" if custom
84
+ lines << " --color-custom-text: #{custom.text};" if custom
68
85
 
69
86
  @keystone_palette_css = ":root {\n#{lines.join("\n")}\n}"
70
87
  end
@@ -78,6 +95,8 @@ module KeystoneUi
78
95
  end
79
96
 
80
97
  def stale_cache?(owner, cached)
98
+ return true unless cached.key?(:text)
99
+
81
100
  updated_at = KeystoneUi::Colors::ThemePreference
82
101
  .where(owner: owner)
83
102
  .pick(:updated_at)
@@ -4,7 +4,6 @@ module KeystoneUi
4
4
  module Colors
5
5
  class SettingsController < ApplicationController
6
6
  def show
7
- @preference = theme_preference
8
7
  end
9
8
 
10
9
  def destroy
@@ -13,40 +12,24 @@ module KeystoneUi
13
12
  end
14
13
 
15
14
  def update
16
- @preference = theme_preference
17
-
18
- if preference_params[:template_name].present?
19
- template = Templates[preference_params[:template_name]]
20
- @preference.assign_attributes(
21
- accent: template[:accent].to_s,
22
- surface: template[:surface].to_s,
23
- template_name: preference_params[:template_name],
24
- mode: preference_params[:mode]
25
- )
26
- else
27
- @preference.assign_attributes(preference_params)
28
- end
29
-
30
- if @preference.save
31
- cookies.delete(KeystoneUi::ThemeChoice::COOKIE)
32
- redirect_to keystone_ui_colors.settings_path, notice: "Color settings updated."
33
- else
34
- render :show, status: :unprocessable_entity
35
- end
15
+ result = PickColours.new(person: current_owner, account: nil, values: preference_params).call
16
+
17
+ return render :show, status: :unprocessable_entity unless result.ok?
18
+
19
+ ForgetTheThemeChoice.new(controller: self).call
20
+ redirect_to keystone_ui_colors.settings_path, notice: "Color settings updated."
36
21
  end
37
22
 
38
23
  private
39
24
 
25
+ helper_method :current_owner
26
+
40
27
  def current_owner
41
28
  send(KeystoneUi::Colors.configuration.current_owner_method)
42
29
  end
43
30
 
44
- def theme_preference
45
- ThemePreference.find_or_initialize_by(owner: current_owner)
46
- end
47
-
48
31
  def preference_params
49
- params.require(:theme_preference).permit(:accent, :surface, :template_name, :mode)
32
+ params.permit(:accent, :surface, :text, :template_name, :mode).to_h.symbolize_keys
50
33
  end
51
34
  end
52
35
  end
@@ -1,11 +1,12 @@
1
1
  import { Controller } from "@hotwired/stimulus"
2
2
 
3
3
  export default class extends Controller {
4
- static targets = ["accentPicker", "surfacePicker", "customRadio", "templateLabel"]
4
+ static targets = ["accentPicker", "surfacePicker", "textPicker", "customRadio", "templateLabel"]
5
5
 
6
6
  connect() {
7
7
  this.accentPickerTarget.addEventListener("change", () => this.customColorChanged())
8
8
  this.surfacePickerTarget.addEventListener("change", () => this.customColorChanged())
9
+ this.textPickerTarget.addEventListener("change", () => this.customColorChanged())
9
10
  }
10
11
 
11
12
  selectTemplate(event) {
@@ -13,9 +14,11 @@ export default class extends Controller {
13
14
  const label = input.closest("[data-template]")
14
15
  const accentHex = input.dataset.accentHex
15
16
  const surfaceHex = input.dataset.surfaceHex
17
+ const textHex = input.dataset.textHex
16
18
 
17
19
  this.setPickerValue(this.accentPickerTarget, accentHex)
18
20
  this.setPickerValue(this.surfacePickerTarget, surfaceHex)
21
+ this.setPickerValue(this.textPickerTarget, textHex)
19
22
 
20
23
  this.highlightTemplate(label.dataset.template)
21
24
  }
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KeystoneUi
4
+ module Colors
5
+ class CustomColours
6
+ def initialize(template_name:, surface:, text:)
7
+ @template_name = template_name
8
+ @surface = surface
9
+ @text = text
10
+ end
11
+
12
+ def background
13
+ return Templates[@template_name][:background] if @template_name.present?
14
+ return @surface if @surface&.start_with?("#")
15
+
16
+ KeystoneUi::Colors.configuration.default_background
17
+ end
18
+
19
+ def text
20
+ return Templates[@template_name][:text] if @template_name.present?
21
+
22
+ @text.presence || KeystoneUi::Colors.configuration.default_text
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KeystoneUi
4
+ module Colors
5
+ class ForgetTheThemeChoice
6
+ def initialize(controller:)
7
+ @controller = controller
8
+ end
9
+
10
+ def call
11
+ @controller.request.cookie_jar.delete(KeystoneUi::ThemeChoice::COOKIE)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KeystoneUi
4
+ module Colors
5
+ class Kept
6
+ def ok? = true
7
+
8
+ def message = nil
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KeystoneUi
4
+ module Colors
5
+ class PickColours
6
+ def initialize(values:, person: nil, account: nil, owner: nil)
7
+ @owner = person || owner
8
+ @account = account
9
+ @values = values
10
+ end
11
+
12
+ def call
13
+ preference.assign_attributes(chosen_colours)
14
+
15
+ return Refusal.new(preference.errors.full_messages.first) unless preference.save
16
+
17
+ Kept.new
18
+ end
19
+
20
+ private
21
+
22
+ def preference
23
+ @preference ||= ThemePreference.find_or_initialize_by(owner: @owner)
24
+ end
25
+
26
+ def chosen_colours
27
+ return { accent: @values[:accent], surface: @values[:surface], text: @values[:text], mode: @values[:mode] }.compact if @values[:template_name].blank?
28
+
29
+ template = Templates[@values[:template_name]]
30
+ {
31
+ accent: template[:accent].to_s,
32
+ surface: template[:surface].to_s,
33
+ template_name: @values[:template_name],
34
+ mode: @values[:mode]
35
+ }.compact
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KeystoneUi
4
+ module Colors
5
+ class Refusal
6
+ attr_reader :message
7
+
8
+ def initialize(message)
9
+ @message = message
10
+ end
11
+
12
+ def ok? = false
13
+ end
14
+ end
15
+ end
@@ -13,12 +13,13 @@ module KeystoneUi
13
13
 
14
14
  HEX_COLOR = /\A#[0-9a-fA-F]{6}\z/
15
15
 
16
- MODES = %w[light dark system].freeze
16
+ MODES = %w[light dark system custom].freeze
17
17
 
18
18
  validate :accent_is_valid
19
19
  validate :surface_is_valid
20
20
  validates :template_name, inclusion: { in: Templates.names.map(&:to_s) }, allow_blank: true
21
21
  validates :mode, inclusion: { in: MODES }, allow_nil: true
22
+ validates :text, format: { with: HEX_COLOR }, allow_blank: true
22
23
 
23
24
  def apply_template!(name)
24
25
  template = Templates[name]
@@ -0,0 +1,107 @@
1
+ <%
2
+ preference = KeystoneUi::Colors::ThemePreference.find_or_initialize_by(owner: person)
3
+
4
+ current_accent_hex = if preference.accent&.start_with?("#")
5
+ preference.accent
6
+ else
7
+ KeystoneUi::Colors::Palettes.accent(preference.accent || "blue")[500]
8
+ end
9
+
10
+ current_surface_hex = if preference.surface&.start_with?("#")
11
+ preference.surface
12
+ else
13
+ KeystoneUi::Colors::Palettes.surface(preference.surface || "zinc")[500]
14
+ end
15
+
16
+ current_text_hex = KeystoneUi::Colors::CustomColours.new(
17
+ template_name: preference.template_name,
18
+ surface: preference.surface,
19
+ text: preference.text
20
+ ).text
21
+
22
+ selected_template = preference.template_name
23
+ custom_selected = preference.accent&.start_with?("#") || (selected_template.blank? && preference.persisted?)
24
+ %>
25
+
26
+ <%= ui_panel(padding: :lg) do %>
27
+ <%= form_with(
28
+ url: submit_url,
29
+ method: :patch,
30
+ data: { turbo: false, controller: "keystone-ui--colors--theme-settings", selected_template: selected_template }
31
+ ) do |form| %>
32
+ <div style="display: flex; flex-direction: column; gap: 2rem;">
33
+ <%= ui_section(title: "Theme", spacing: :sm) do %>
34
+ <div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
35
+ <% KeystoneUi::Colors::Templates.names.each do |name| %>
36
+ <% template = KeystoneUi::Colors::Templates[name] %>
37
+ <% accent_hex = KeystoneUi::Colors::Palettes.accent(template[:accent])[500] %>
38
+ <% surface_hex = KeystoneUi::Colors::Palettes.surface(template[:surface])[500] %>
39
+ <%= ui_option_card(
40
+ name: "template_name",
41
+ value: name,
42
+ selected: selected_template == name.to_s,
43
+ input_data: {
44
+ action: "keystone-ui--colors--theme-settings#selectTemplate",
45
+ "accent-hex": accent_hex,
46
+ "surface-hex": surface_hex,
47
+ "text-hex": template[:text]
48
+ },
49
+ label_data: {
50
+ "keystone-ui--colors--theme-settings-target": "templateLabel",
51
+ template: name
52
+ }
53
+ ) do %>
54
+ <span style="display: inline-block; width: 1rem; height: 1rem; border-radius: 9999px; background-color: <%= accent_hex %>;"></span>
55
+ <span style="display: inline-block; width: 1rem; height: 1rem; border-radius: 0.25rem; background-color: <%= surface_hex %>;"></span>
56
+ <span><%= template[:label] %></span>
57
+ <% end %>
58
+ <% end %>
59
+ <%= ui_option_card(
60
+ name: "template_name",
61
+ value: "",
62
+ selected: custom_selected,
63
+ input_data: {
64
+ "keystone-ui--colors--theme-settings-target": "customRadio"
65
+ },
66
+ label_data: {
67
+ "keystone-ui--colors--theme-settings-target": "templateLabel",
68
+ template: "custom"
69
+ }
70
+ ) do %>
71
+ <span>Custom</span>
72
+ <% end %>
73
+ </div>
74
+ <% end %>
75
+
76
+ <%= ui_section(title: "Accent Color", spacing: :sm) do %>
77
+ <div data-keystone-ui--colors--theme-settings-target="accentPicker">
78
+ <%= ui_color_picker(name: "accent", value: current_accent_hex) %>
79
+ </div>
80
+ <% end %>
81
+
82
+ <%= ui_section(title: "Surface", spacing: :sm) do %>
83
+ <div data-keystone-ui--colors--theme-settings-target="surfacePicker">
84
+ <%= ui_color_picker(name: "surface", value: current_surface_hex) %>
85
+ </div>
86
+ <% end %>
87
+
88
+ <%= ui_section(title: "Text Color", spacing: :sm) do %>
89
+ <div data-keystone-ui--colors--theme-settings-target="textPicker">
90
+ <%= ui_color_picker(name: "text", value: current_text_hex) %>
91
+ </div>
92
+ <% end %>
93
+
94
+ <%= ui_section(title: "Mode", spacing: :sm) do %>
95
+ <div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
96
+ <% KeystoneUi::Colors::ThemePreference::MODES.each do |mode| %>
97
+ <%= ui_radio_card(name: "mode", value: mode, label: mode.capitalize, checked: (preference.mode || KeystoneUi::Colors.configuration.default_mode) == mode) %>
98
+ <% end %>
99
+ </div>
100
+ <% end %>
101
+
102
+ <div>
103
+ <%= ui_button(label: "Save", type: :submit) %>
104
+ </div>
105
+ </div>
106
+ <% end %>
107
+ <% end %>
@@ -4,96 +4,4 @@
4
4
  <%= ui_alert(message: flash[:notice], type: :success) %>
5
5
  <% end %>
6
6
 
7
- <%
8
- current_accent_hex = if @preference.accent&.start_with?("#")
9
- @preference.accent
10
- else
11
- KeystoneUi::Colors::Palettes.accent(@preference.accent || "blue")[500]
12
- end
13
-
14
- current_surface_hex = if @preference.surface&.start_with?("#")
15
- @preference.surface
16
- else
17
- KeystoneUi::Colors::Palettes.surface(@preference.surface || "zinc")[500]
18
- end
19
-
20
- selected_template = @preference.template_name
21
- custom_selected = @preference.accent&.start_with?("#") || (selected_template.blank? && @preference.persisted?)
22
- %>
23
-
24
- <%= ui_panel(padding: :lg) do %>
25
- <%= form_with(
26
- url: keystone_ui_colors.settings_path,
27
- method: :patch,
28
- scope: :theme_preference,
29
- data: { turbo: false, controller: "keystone-ui--colors--theme-settings", selected_template: selected_template }
30
- ) do |form| %>
31
- <div style="display: flex; flex-direction: column; gap: 2rem;">
32
- <%= ui_section(title: "Theme", spacing: :sm) do %>
33
- <div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
34
- <% KeystoneUi::Colors::Templates.names.each do |name| %>
35
- <% template = KeystoneUi::Colors::Templates[name] %>
36
- <% accent_hex = KeystoneUi::Colors::Palettes.accent(template[:accent])[500] %>
37
- <% surface_hex = KeystoneUi::Colors::Palettes.surface(template[:surface])[500] %>
38
- <%= ui_option_card(
39
- name: "theme_preference[template_name]",
40
- value: name,
41
- selected: selected_template == name.to_s,
42
- input_data: {
43
- action: "keystone-ui--colors--theme-settings#selectTemplate",
44
- "accent-hex": accent_hex,
45
- "surface-hex": surface_hex
46
- },
47
- label_data: {
48
- "keystone-ui--colors--theme-settings-target": "templateLabel",
49
- template: name
50
- }
51
- ) do %>
52
- <span style="display: inline-block; width: 1rem; height: 1rem; border-radius: 9999px; background-color: <%= accent_hex %>;"></span>
53
- <span style="display: inline-block; width: 1rem; height: 1rem; border-radius: 0.25rem; background-color: <%= surface_hex %>;"></span>
54
- <span><%= template[:label] %></span>
55
- <% end %>
56
- <% end %>
57
- <%= ui_option_card(
58
- name: "theme_preference[template_name]",
59
- value: "",
60
- selected: custom_selected,
61
- input_data: {
62
- "keystone-ui--colors--theme-settings-target": "customRadio"
63
- },
64
- label_data: {
65
- "keystone-ui--colors--theme-settings-target": "templateLabel",
66
- template: "custom"
67
- }
68
- ) do %>
69
- <span>Custom</span>
70
- <% end %>
71
- </div>
72
- <% end %>
73
-
74
- <%= ui_section(title: "Accent Color", spacing: :sm) do %>
75
- <div data-keystone-ui--colors--theme-settings-target="accentPicker">
76
- <%= ui_color_picker(name: "theme_preference[accent]", value: current_accent_hex) %>
77
- </div>
78
- <% end %>
79
-
80
- <%= ui_section(title: "Surface", spacing: :sm) do %>
81
- <div data-keystone-ui--colors--theme-settings-target="surfacePicker">
82
- <%= ui_color_picker(name: "theme_preference[surface]", value: current_surface_hex) %>
83
- </div>
84
- <% end %>
85
-
86
- <%= ui_section(title: "Mode", spacing: :sm) do %>
87
- <div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
88
- <% KeystoneUi::Colors::ThemePreference::MODES.each do |mode| %>
89
- <%= ui_radio_card(name: "theme_preference[mode]", value: mode, label: mode.capitalize, checked: (@preference.mode || KeystoneUi::Colors.configuration.default_mode) == mode) %>
90
- <% end %>
91
- </div>
92
- <% end %>
93
-
94
- <div>
95
- <%= ui_button(label: "Save", type: :submit) %>
96
- </div>
97
- </div>
98
- <% end %>
99
- <% end %>
7
+ <%= render partial: "keystone_ui/colors/settings/picker", locals: {person: current_owner, account: nil, submit_url: keystone_ui_colors.settings_path} %>
@@ -5,6 +5,7 @@ class CreateKeystoneUiColorsThemePreferences < ActiveRecord::Migration[<%= Activ
5
5
  t.string :surface, null: false
6
6
  t.string :template_name
7
7
  t.string :mode
8
+ t.string :text
8
9
  t.references :owner, polymorphic: true, null: false
9
10
  t.timestamps
10
11
  end
@@ -0,0 +1,5 @@
1
+ class AddTextToKeystoneUiColorsThemePreferences < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ add_column :keystone_ui_colors_theme_preferences, :text, :string
4
+ end
5
+ end
@@ -20,6 +20,13 @@ module KeystoneUi
20
20
  )
21
21
  end
22
22
 
23
+ def add_text_migration
24
+ migration_template(
25
+ File.expand_path("templates/add_text_to_keystone_ui_colors_theme_preferences.rb.erb", __dir__),
26
+ "db/migrate/add_text_to_keystone_ui_colors_theme_preferences.rb"
27
+ )
28
+ end
29
+
23
30
  def copy_stimulus_controller
24
31
  copy_file "theme_settings_controller.js",
25
32
  "app/javascript/controllers/keystone_ui/colors/theme_settings_controller.js"
@@ -3,7 +3,7 @@
3
3
  module KeystoneUi
4
4
  module Colors
5
5
  class Configuration
6
- attr_accessor :current_owner_method, :authentication_method, :default_template, :default_accent, :default_surface, :default_mode, :layout
6
+ attr_accessor :current_owner_method, :authentication_method, :default_template, :default_accent, :default_surface, :default_mode, :default_background, :default_text, :layout
7
7
 
8
8
  def initialize
9
9
  @current_owner_method = :current_user
@@ -12,6 +12,8 @@ module KeystoneUi
12
12
  @default_accent = "blue"
13
13
  @default_surface = "zinc"
14
14
  @default_mode = "light"
15
+ @default_background = "#ffffff"
16
+ @default_text = "#18181b"
15
17
  @layout = "application"
16
18
  end
17
19
  end
@@ -13,6 +13,10 @@ module KeystoneUi
13
13
  end
14
14
  end
15
15
 
16
+ initializer "keystone_ui.colors.tailwind" do
17
+ KeystoneUi.configuration.tailwind_sources << root.join("app/views/**/*.erb").to_s
18
+ end
19
+
16
20
  initializer "keystone_ui.colors.url_helpers" do
17
21
  ActiveSupport.on_load(:action_controller) do
18
22
  helper Rails.application.routes.url_helpers
@@ -7,30 +7,40 @@ module KeystoneUi
7
7
  ocean: {
8
8
  accent: :blue,
9
9
  surface: :slate,
10
+ background: "#e0f2fe",
11
+ text: "#0c4a6e",
10
12
  label: "Ocean",
11
13
  description: "Cool blues with slate undertones"
12
14
  },
13
15
  forest: {
14
16
  accent: :emerald,
15
17
  surface: :stone,
18
+ background: "#ecfdf5",
19
+ text: "#064e3b",
16
20
  label: "Forest",
17
21
  description: "Natural greens with warm stone"
18
22
  },
19
23
  twilight: {
20
24
  accent: :violet,
21
25
  surface: :zinc,
26
+ background: "#1e1b4b",
27
+ text: "#ede9fe",
22
28
  label: "Twilight",
23
29
  description: "Deep violet with clean zinc"
24
30
  },
25
31
  coral: {
26
32
  accent: :rose,
27
33
  surface: :neutral,
34
+ background: "#fff1f2",
35
+ text: "#4c0519",
28
36
  label: "Coral",
29
37
  description: "Warm rose with neutral balance"
30
38
  },
31
39
  arctic: {
32
40
  accent: :cyan,
33
41
  surface: :gray,
42
+ background: "#ecfeff",
43
+ text: "#164e63",
34
44
  label: "Arctic",
35
45
  description: "Bright cyan with crisp gray"
36
46
  }
@@ -54,6 +64,8 @@ module KeystoneUi
54
64
  {
55
65
  accent: KeystoneUi::Colors.configuration.default_accent,
56
66
  surface: KeystoneUi::Colors.configuration.default_surface,
67
+ background: KeystoneUi::Colors.configuration.default_background,
68
+ text: KeystoneUi::Colors.configuration.default_text,
57
69
  label: "Default",
58
70
  description: "Default theme"
59
71
  }
@@ -2,6 +2,6 @@
2
2
 
3
3
  module KeystoneUi
4
4
  module Colors
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keystone_ui-colors
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Schneider
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-09-14 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: keystone_ui
@@ -16,14 +15,14 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: 0.10.0
18
+ version: 0.11.0
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: 0.10.0
25
+ version: 0.11.0
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: railties
29
28
  requirement: !ruby/object:Gem::Requirement
@@ -83,12 +82,19 @@ files:
83
82
  - app/helpers/keystone_ui/colors/palette_helper.rb
84
83
  - app/javascript/keystone_ui/colors/theme_settings_controller.js
85
84
  - app/models/keystone_ui/colors/application_record.rb
85
+ - app/models/keystone_ui/colors/custom_colours.rb
86
+ - app/models/keystone_ui/colors/forget_the_theme_choice.rb
87
+ - app/models/keystone_ui/colors/kept.rb
88
+ - app/models/keystone_ui/colors/pick_colours.rb
89
+ - app/models/keystone_ui/colors/refusal.rb
86
90
  - app/models/keystone_ui/colors/theme_preference.rb
91
+ - app/views/keystone_ui/colors/settings/_picker.html.erb
87
92
  - app/views/keystone_ui/colors/settings/show.html.erb
88
93
  - config/routes.rb
89
94
  - lib/generators/keystone_ui/colors/install/install_generator.rb
90
95
  - lib/generators/keystone_ui/colors/install/templates/create_keystone_ui_colors_theme_preferences.rb.erb
91
96
  - lib/generators/keystone_ui/colors/update/templates/add_mode_to_keystone_ui_colors_theme_preferences.rb.erb
97
+ - lib/generators/keystone_ui/colors/update/templates/add_text_to_keystone_ui_colors_theme_preferences.rb.erb
92
98
  - lib/generators/keystone_ui/colors/update/update_generator.rb
93
99
  - lib/keystone_ui-colors.rb
94
100
  - lib/keystone_ui/colors.rb
@@ -107,7 +113,6 @@ metadata:
107
113
  bug_tracker_uri: https://github.com/DYB-Development/keystone_ui-colors/issues
108
114
  documentation_uri: https://github.com/DYB-Development/keystone_ui-colors#readme
109
115
  rubygems_mfa_required: 'true'
110
- post_install_message:
111
116
  rdoc_options: []
112
117
  require_paths:
113
118
  - lib
@@ -122,8 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
127
  - !ruby/object:Gem::Version
123
128
  version: '0'
124
129
  requirements: []
125
- rubygems_version: 3.5.16
126
- signing_key:
130
+ rubygems_version: 4.0.20
127
131
  specification_version: 4
128
132
  summary: Per-user color palette settings for Keystone UI.
129
133
  test_files: []