utility_palettes 0.1.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 +7 -0
- data/README.md +123 -0
- data/Rakefile +8 -0
- data/lib/generators/templates/config/utility_palettes.yml +36 -0
- data/lib/generators/utility_palettes/config_generator.rb +13 -0
- data/lib/generators/utility_palettes/generate_generator.rb +35 -0
- data/lib/utility_palettes/configuration.rb +25 -0
- data/lib/utility_palettes/defaults.rb +46 -0
- data/lib/utility_palettes/outputs.rb +122 -0
- data/lib/utility_palettes/palettes.rb +133 -0
- data/lib/utility_palettes/sequences.rb +90 -0
- data/lib/utility_palettes/swatch.rb +108 -0
- data/lib/utility_palettes/validations.rb +24 -0
- data/lib/utility_palettes/version.rb +5 -0
- data/lib/utility_palettes.rb +16 -0
- metadata +74 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 34b5bd3eb2c8d5da22989e14983ccb8e47eb9978f10d50bd8e2b5bf77c15e3d5
|
4
|
+
data.tar.gz: 0ff299a1d1f60b7f05111496023a23ced487f06c2ec38fcf750c94dc4e9bd1a1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 04c3c7333c77242ce88fcc0cbdf4aef4bf7ce0bda9e1fcf3f2c55a15c541f6767038d69b6036689dcef17c59b43444ebd0f80b392236d58a99c11efb2e447234
|
7
|
+
data.tar.gz: 2c40080c0e2aa1eb7746eadfebefa661007864c095d393de758167573354fd67a8e1dcf539fdc2a7203fdb1e758abbdcf1c2b57f5a1ff82e76bac226f387f6a4
|
data/README.md
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# UtilityPalettes
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/utility_palettes`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
> Generate your own colour palettes in an instance.
|
6
|
+
|
7
|
+
Utility Palettes is an ruby gem package for use in ruby or other projects that generates palettes and shades based on supplied colours.
|
8
|
+
Take a single colour and generate a full tailwind-style set of "absolute" shades ranging from -50 to -900 where the given colour is inserted into the range where it suits best.
|
9
|
+
Or generate "relative" shades that have the supplied colour at it's core and add a -light and -dark shade.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Install the gem and add to the application's Gemfile by executing:
|
14
|
+
|
15
|
+
```bash
|
16
|
+
bundle add utility_palettes
|
17
|
+
```
|
18
|
+
|
19
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
gem install utility_palettes
|
23
|
+
```
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
To get the config full of
|
28
|
+
|
29
|
+
```bash
|
30
|
+
rails generate utility_palettes:config
|
31
|
+
```
|
32
|
+
|
33
|
+
to generate the palettes
|
34
|
+
|
35
|
+
```bash
|
36
|
+
rails generate utility_palettes:generate
|
37
|
+
```
|
38
|
+
|
39
|
+
### Environment
|
40
|
+
|
41
|
+
Set the environment that the sub-data will be read in, meaning that the palette generator can only be run in certain environments.
|
42
|
+
|
43
|
+
### Defaults
|
44
|
+
|
45
|
+
Should the default colours be included for the palette generator. Even if they are included, they can be overwritten by the user's own colour of the same name:
|
46
|
+
|
47
|
+
| Option | Default | Possible Values |
|
48
|
+
| :--------- | :-----: | :-------------: |
|
49
|
+
| absolutes | true | true / false |
|
50
|
+
| relatives | true | true / false |
|
51
|
+
| singles | true | true / false |
|
52
|
+
|
53
|
+
### Output
|
54
|
+
|
55
|
+
How the output file and values should be written:
|
56
|
+
|
57
|
+
| Option | Description | Default | Possible Values |
|
58
|
+
| :------ | :-------------------------------------------------------------------------------------- | :-----: | :-----------------------------------------------------: |
|
59
|
+
| dated | Should a timestamp be included in the filenames | false | true / false |
|
60
|
+
| files | The types of files the palette should be output in, written as a comma separated string | json | json, scss, css |
|
61
|
+
| format | The colour syntax the output colours should be written in | rgb | rgb, hsl, hsv, hsb, cmyk, cielab, lab, cielch, lch, hex |
|
62
|
+
| prefix | A string that is appended to the start of all colour names, i.e. 'tw-' | '' | <any string> |
|
63
|
+
| suffix | A string that is appended to the end of all colour names, i.e. '-col' | '' | <any string> |
|
64
|
+
|
65
|
+
The JSON output file will appear at the top-level of your project, while SCSS and CSS are defined to go to `app/assets/stylesheets`.
|
66
|
+
|
67
|
+
### Method (WIP)
|
68
|
+
|
69
|
+
It allos you to determine how you want the colours to be adjusted to create the variance in your palette, but for now all colour adjustments are made by changing the HSL values as it is the best combination of the simple yet effective and accurate method available.
|
70
|
+
In the future you will be able to shift the colours by changing values for different colour models and colour spaces; like RGB, CieLAB, OkLCH and others.
|
71
|
+
|
72
|
+
### Steps (WIP)
|
73
|
+
|
74
|
+
Defines the percentage you want each colour to be adjusted by when moving through the colour palette. For now all steps are for the HSL method of adjusting colours
|
75
|
+
In the future you will be able to shift the colours by changing values for different colour models and colour spaces; like RGB, CieLAB, OkLCH and others.
|
76
|
+
|
77
|
+
| Option | Description | Default | Possible Values |
|
78
|
+
| :------ | :----------------------------------: | :-----: | :-------------: |
|
79
|
+
| h | How much to adjust the hue by | 0 | 0 - 100 |
|
80
|
+
| s | How much to adjust the saturation by | 2 | 0 - 100 |
|
81
|
+
| l | How much to adjust the lightness by | 9 | 0 - 100 |
|
82
|
+
|
83
|
+
### Absolutes
|
84
|
+
|
85
|
+
Here you would define colour names and values that you would like to create an "absolute palette"; where for each colour given, a range of colours are output with -50, -100, ..., -900 suffixes using that colour as the base.
|
86
|
+
|
87
|
+
### Relatives
|
88
|
+
|
89
|
+
Here you would define colour names and values that you would like to create an "relative palette"; where for each colour given, a -light and -dark colour are generated one step up and down from the base colour.
|
90
|
+
If a colour is included in both the "absolutes" and "relatives" sections, then the -light and -dark colours will match with colours in the absolute palette.
|
91
|
+
|
92
|
+
### Singles
|
93
|
+
|
94
|
+
Here you would define colour names and values that you would like copied directly to the output as you have defined them.
|
95
|
+
|
96
|
+
## Pipeline
|
97
|
+
|
98
|
+
Things that will hopefully be added in future development:
|
99
|
+
|
100
|
+
- Finishing the overall palette spec file
|
101
|
+
- Migrate off the reliance of Rails Generators
|
102
|
+
- Different colour model and space methods for adapting colours, and the steps to go with them
|
103
|
+
- Add more defaults such as different Tailwind version colours
|
104
|
+
- Have a view that can be copied to apps to allow users the ability to quickly review the colours generated
|
105
|
+
- Finish the '$' copy functionality to use colours defined in the config for other colours later in the generation process
|
106
|
+
|
107
|
+
## Development
|
108
|
+
|
109
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
110
|
+
|
111
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
112
|
+
|
113
|
+
## Contributing
|
114
|
+
|
115
|
+
Bug reports and pull requests are welcome on GitHub at <https://github.com/louiswdavis/utility_palettes>. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/louiswdavis/utility_palettes/blob/master/CODE_OF_CONDUCT.md).
|
116
|
+
|
117
|
+
## License
|
118
|
+
|
119
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
120
|
+
|
121
|
+
## Code of Conduct
|
122
|
+
|
123
|
+
Everyone interacting in the UtilityPalettes project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/louiswdavis/utility_palettes/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
development:
|
2
|
+
utility_palettes:
|
3
|
+
defaults:
|
4
|
+
absolutes: true
|
5
|
+
relatives: true
|
6
|
+
singles: true
|
7
|
+
output:
|
8
|
+
dated: false
|
9
|
+
files: 'json, scss, css'
|
10
|
+
format: 'hex'
|
11
|
+
prefix: ''
|
12
|
+
suffix: ''
|
13
|
+
method: 'hsl'
|
14
|
+
steps:
|
15
|
+
h: 0
|
16
|
+
s: 3
|
17
|
+
l: 8
|
18
|
+
absolutes:
|
19
|
+
teal: '#2cb1bc'
|
20
|
+
orange: '#e35f00'
|
21
|
+
green: '#28b840'
|
22
|
+
purple: '#4f3bbf'
|
23
|
+
relatives:
|
24
|
+
success: '#28b840'
|
25
|
+
information: '#2cb1bc'
|
26
|
+
singles:
|
27
|
+
grey-50: '#fafafa'
|
28
|
+
grey-100: '#f5f5f5'
|
29
|
+
grey-200: '#e5e5e5'
|
30
|
+
grey-300: '#d4d4d4'
|
31
|
+
grey-400: '#a3a3a3'
|
32
|
+
grey-500: '#737373'
|
33
|
+
grey-600: '#525252'
|
34
|
+
grey-700: '#404040'
|
35
|
+
grey-800: '#262626'
|
36
|
+
grey-900: '#171717'
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UtilityPalettes
|
4
|
+
module Generators
|
5
|
+
class ConfigGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('../../..', __dir__)
|
7
|
+
|
8
|
+
def copy_config
|
9
|
+
copy_file 'lib/generators/templates/config/utility_palettes.yml', 'config/utility_palettes.yml'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UtilityPalettes
|
4
|
+
module Generators
|
5
|
+
class GenerateGenerator < Rails::Generators::Base
|
6
|
+
def generate_utility_palettes
|
7
|
+
config = {}
|
8
|
+
|
9
|
+
if File.exist?('config/utility_palettes.yml') && defined?(Rails.application.config_for)
|
10
|
+
config = Rails.application.config_for(:utility_palettes).dig('utility_palettes')
|
11
|
+
elsif File.exist?('config/utility_palettes.json')
|
12
|
+
config = JSON.parse(File.read('config/utility_palettes.json')).dig(Rails.env.to_s, 'utility_palettes')
|
13
|
+
end
|
14
|
+
|
15
|
+
if !config.is_a?(Hash)
|
16
|
+
self.class.config_format_warn
|
17
|
+
elsif config.dig('disabled') == true
|
18
|
+
self.class.disabled_warn
|
19
|
+
else
|
20
|
+
UtilityPalettes::Palettes.generate(config)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def self.config_format_warn
|
27
|
+
warn 'ERROR: Utility Palettes config is not formatted as a hash for the "utility_palettes" value'
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.disabled_warn
|
31
|
+
warn 'ERROR: Utility Palettes is disabled for this environment'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UtilityPalettes
|
4
|
+
class Configuration
|
5
|
+
def self.setup(config)
|
6
|
+
# default increment steps
|
7
|
+
default_increments = UtilityPalettes::Configuration.defaults
|
8
|
+
|
9
|
+
user_increments = config.dig(:steps) || {}
|
10
|
+
|
11
|
+
h_step = user_increments.dig(:h) || default_increments.dig('hsl', :h)
|
12
|
+
s_step = user_increments.dig(:s) || default_increments.dig('hsl', :s)
|
13
|
+
l_step = user_increments.dig(:l) || default_increments.dig('hsl', :l)
|
14
|
+
|
15
|
+
{ h_step: h_step, s_step: s_step, l_step: l_step }
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.defaults
|
19
|
+
{
|
20
|
+
'rgb' => { r: '7%', g: '7%', b: '7%' },
|
21
|
+
'hsl' => { h: 0, s: 2, l: 9 }
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UtilityPalettes
|
4
|
+
class Defaults
|
5
|
+
def self.absolutes
|
6
|
+
{
|
7
|
+
'red' => 'hsl(2, 78%, 64%)',
|
8
|
+
'rust' => 'hsl(16, 82%, 62%)',
|
9
|
+
'orange' => 'hsl(31, 90%, 65%)',
|
10
|
+
'gold' => 'hsl(46, 93%, 54%)',
|
11
|
+
'yellow' => 'hsl(58, 87%, 55%)',
|
12
|
+
'pear' => 'hsl(80, 74%, 57%)',
|
13
|
+
'green' => 'hsl(110, 69%, 58%)',
|
14
|
+
'seaside' => 'hsl(156, 78%, 57%)',
|
15
|
+
'cyan' => 'hsl(180, 69%, 37%)',
|
16
|
+
'capri' => 'hsl(197, 90%, 46%)',
|
17
|
+
'blue' => 'hsl(214, 78%, 36%)',
|
18
|
+
'iris' => 'hsl(265, 87%, 57%)',
|
19
|
+
'purple' => 'hsl(279, 85%, 56%)',
|
20
|
+
'magenta' => 'hsl(300, 64%, 66%)',
|
21
|
+
'pink' => 'hsl(320, 74%, 66%)',
|
22
|
+
'satin' => 'hsl(348, 74%, 57%)',
|
23
|
+
'cement' => 'hsl(42, 6%, 87%)',
|
24
|
+
'grey' => 'hsl(0, 3%, 46%)',
|
25
|
+
'base' => 'hsl(0, 3%, 46%)'
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.relatives
|
30
|
+
{
|
31
|
+
'success' => 'hsl(110, 69%, 58%)',
|
32
|
+
'danger' => 'hsl(2, 78%, 64%)',
|
33
|
+
'information' => 'hsl(214, 78%, 36%)',
|
34
|
+
'warning' => 'hsl(46, 93%, 54%)'
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.singles
|
39
|
+
{
|
40
|
+
'white' => '#fff',
|
41
|
+
'black' => '#000',
|
42
|
+
'translucent' => 'rgba(0, 0, 0, 0.45)'
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UtilityPalettes
|
4
|
+
class Outputs
|
5
|
+
def self.generate(generated_palettes, config)
|
6
|
+
output_format = config.dig(:output, :format).to_s.downcase.presence
|
7
|
+
output_palettes = {}
|
8
|
+
|
9
|
+
generated_palettes.each do |label, colourize_colour|
|
10
|
+
case output_format
|
11
|
+
when 'rgb'
|
12
|
+
values = colourize_colour.rgb.values
|
13
|
+
when 'hsl'
|
14
|
+
values = colourize_colour.hsl.values
|
15
|
+
values[1] = self.append_percentage(values[1])
|
16
|
+
values[2] = self.append_percentage(values[2])
|
17
|
+
when 'hsv'
|
18
|
+
values = colourize_colour.hsv.values
|
19
|
+
values[1] = self.append_percentage(values[1])
|
20
|
+
values[2] = self.append_percentage(values[2])
|
21
|
+
when 'hsb'
|
22
|
+
values = colourize_colour.hsb.values
|
23
|
+
values[1] = self.append_percentage(values[1])
|
24
|
+
values[2] = self.append_percentage(values[2])
|
25
|
+
when 'cmyk'
|
26
|
+
values = colourize_colour.cmyk.values
|
27
|
+
values[1] = self.append_percentage(values[1])
|
28
|
+
values[2] = self.append_percentage(values[2])
|
29
|
+
values[3] = self.append_percentage(values[3])
|
30
|
+
output_format = 'device-cmyk'
|
31
|
+
when 'cielab', 'lab'
|
32
|
+
values = colourize_colour.cielab.values
|
33
|
+
values[0] = self.append_percentage(values[0])
|
34
|
+
output_format = 'lab'
|
35
|
+
when 'cielch', 'lch'
|
36
|
+
values = colourize_colour.cielch.values
|
37
|
+
values[0] = self.append_percentage(values[0])
|
38
|
+
output_format = 'lch'
|
39
|
+
when 'hex'
|
40
|
+
values = colourize_colour.hex
|
41
|
+
else
|
42
|
+
values = colourize_colour.rgb.values
|
43
|
+
output_format = 'rgb'
|
44
|
+
end
|
45
|
+
|
46
|
+
case output_format
|
47
|
+
when 'hex'
|
48
|
+
values += (colourize_colour.alpha * 255).round.to_s(16).slice(0, 2).upcase if colourize_colour.alpha < 1.0
|
49
|
+
output_palettes[label] = values
|
50
|
+
else
|
51
|
+
values << self.append_alpha(colourize_colour.alpha) if colourize_colour.alpha < 1.0
|
52
|
+
output_palettes[label] = "#{output_format}(#{values.join(' ')})"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
output_palettes
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.json(filename, output_palettes)
|
60
|
+
file = Tempfile.new([filename, '.json'])
|
61
|
+
|
62
|
+
File.open(file, 'w') do |f|
|
63
|
+
f.write(JSON.pretty_generate(output_palettes))
|
64
|
+
end
|
65
|
+
|
66
|
+
puts 'Exporting utility palettes JSON...'
|
67
|
+
file
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.scss(filename, output_palettes)
|
71
|
+
file = Tempfile.new([filename, '.scss'])
|
72
|
+
|
73
|
+
File.open(file, 'w') do |f|
|
74
|
+
f.write(output_palettes.collect { |label, hex| "$#{label}: #{hex};" }.join("\n"))
|
75
|
+
end
|
76
|
+
|
77
|
+
puts 'Exporting utility palettes SCSS...'
|
78
|
+
file
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.css(filename, output_palettes)
|
82
|
+
file = Tempfile.new([filename, '.css'])
|
83
|
+
|
84
|
+
File.open(file, 'w') do |f|
|
85
|
+
f.write(":root {\n\t#{output_palettes.collect { |label, hex| "--#{label}: #{hex};" }.join("\n\t")}\n}")
|
86
|
+
end
|
87
|
+
|
88
|
+
puts 'Exporting utility palettes CSS...'
|
89
|
+
file
|
90
|
+
end
|
91
|
+
|
92
|
+
def bespoke_property_variables
|
93
|
+
# const utilities = {
|
94
|
+
# bg: (value) => ({ 'background-color': value }),
|
95
|
+
# text: (value) => ({ 'color': value }),
|
96
|
+
# border: (value) => ({ 'border-color': value }),
|
97
|
+
# 'border-t': (value) => ({ '--tw-border-opacity': 1, 'border-top-color': value }),
|
98
|
+
# 'border-r': (value) => ({ '--tw-border-opacity': 1, 'border-right-color': value }),
|
99
|
+
# 'border-b': (value) => ({ '--tw-border-opacity': 1, 'border-bottom-color': value }),
|
100
|
+
# 'border-l': (value) => ({ '--tw-border-opacity': 1, 'border-left-color': value }),
|
101
|
+
# outline: (value) => ({ 'outline-color': value }),
|
102
|
+
# ring: (value) => ({ '--tw-ring-opacity': 1, '--tw-ring-color': value }),
|
103
|
+
# 'ring-offset': (value) => ({ '--tw-ring-offset-color': value }),
|
104
|
+
# 'shadow': (value) => ({ '--tw-shadow-color': value, '--tw-shadow': 'var(--tw-shadow-colored)' }),
|
105
|
+
# accent: (value) => ({ 'accent-color': value }),
|
106
|
+
# caret: (value) => ({ 'caret-color': value }),
|
107
|
+
# fill: (value) => ({ 'fill': value }),
|
108
|
+
# stroke: (value) => ({ 'stroke': value }),
|
109
|
+
# };
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def self.append_percentage(value)
|
115
|
+
"#{value}%"
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.append_alpha(alpha)
|
119
|
+
"/ #{alpha * 100}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UtilityPalettes
|
4
|
+
class Palettes
|
5
|
+
def self.generate(config)
|
6
|
+
@config = config
|
7
|
+
|
8
|
+
puts 'Generating utility palettes...'
|
9
|
+
|
10
|
+
UtilityPalettes::Validations.validate_config(@config)
|
11
|
+
@increment_steppers = UtilityPalettes::Configuration.setup(@config)
|
12
|
+
|
13
|
+
puts 'Retrieved configuration...'
|
14
|
+
|
15
|
+
default_absolutes = @config.dig(:defaults, :absolutes) == false ? [] : UtilityPalettes::Defaults.absolutes
|
16
|
+
default_relatives = @config.dig(:defaults, :relatives) == false ? [] : UtilityPalettes::Defaults.relatives
|
17
|
+
default_singles = @config.dig(:defaults, :singles) == false ? [] : UtilityPalettes::Defaults.singles
|
18
|
+
|
19
|
+
puts 'Defined default palettes...'
|
20
|
+
|
21
|
+
user_absolutes = @config.dig(:absolutes) || {}
|
22
|
+
user_relatives = @config.dig(:relatives) || {}
|
23
|
+
user_singles = @config.dig(:singles) || {}
|
24
|
+
|
25
|
+
puts 'Retrieved user palettes...'
|
26
|
+
|
27
|
+
# merging should mean any user colours will overwrite default colours with the same key names
|
28
|
+
combined_absolutes = default_absolutes.merge(user_absolutes)
|
29
|
+
combined_relatives = default_relatives.merge(user_relatives)
|
30
|
+
combined_singles = {}.merge(default_absolutes, user_absolutes, default_singles, user_singles)
|
31
|
+
|
32
|
+
@combined_samples = combined_absolutes.merge(combined_relatives).merge(combined_singles)
|
33
|
+
|
34
|
+
puts 'Merged palettes...'
|
35
|
+
|
36
|
+
converted_absolutes = self.colourize_values(combined_absolutes)
|
37
|
+
converted_relatives = self.colourize_values(combined_relatives)
|
38
|
+
converted_singles = self.colourize_values(combined_singles)
|
39
|
+
|
40
|
+
puts 'Converted palettes...'
|
41
|
+
|
42
|
+
generated_absolutes = self.palette_looper(converted_absolutes, 'absolutes')
|
43
|
+
generated_relatives = self.palette_looper(converted_relatives, 'relatives')
|
44
|
+
generated_singles = converted_singles
|
45
|
+
|
46
|
+
puts 'Generated utility palettes...'
|
47
|
+
|
48
|
+
generated_palettes = {}.merge(generated_absolutes, generated_relatives, generated_singles)
|
49
|
+
generated_palettes = self.format_palette(generated_palettes)
|
50
|
+
output_palettes = UtilityPalettes::Outputs.generate(generated_palettes, @config)
|
51
|
+
|
52
|
+
filename = 'utility_palettes'
|
53
|
+
filename += "-#{Time.zone.now.strftime('%Y%m%d-%H%M%S')}" if @config.dig(:output, :dated) == true
|
54
|
+
|
55
|
+
output_files = (@config.dig(:output, :files) || '').split(',').map(&:strip)
|
56
|
+
|
57
|
+
file = nil
|
58
|
+
file = UtilityPalettes::Outputs.json(filename, output_palettes) if output_files.blank? || output_files.include?('json')
|
59
|
+
File.rename(file.path, "#{filename}.json") if file.present?
|
60
|
+
|
61
|
+
file = nil
|
62
|
+
file = UtilityPalettes::Outputs.scss(filename, output_palettes) if output_files.include?('scss')
|
63
|
+
File.rename(file.path, "app/assets/stylesheets/#{filename}.scss") if file.present?
|
64
|
+
|
65
|
+
file = nil
|
66
|
+
file = UtilityPalettes::Outputs.css(filename, output_palettes) if output_files.include?('css')
|
67
|
+
File.rename(file.path, "app/assets/stylesheets/#{filename}.css") if file.present?
|
68
|
+
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def self.colourize_values(colour_hash)
|
75
|
+
colourized_hash = {}
|
76
|
+
|
77
|
+
colour_hash.each do |label, colour|
|
78
|
+
begin
|
79
|
+
if colour.start_with?('$')
|
80
|
+
# if the colour label begins with $ then it is a reference to a different defined colour, so must be looked up in the main
|
81
|
+
colourized_hash[label] = ColorConverters::Color.new(@combined_samples.dig(colour[1..].to_sym))
|
82
|
+
else
|
83
|
+
colourized_hash[label] = ColorConverters::Color.new(colour)
|
84
|
+
end
|
85
|
+
|
86
|
+
# TODO
|
87
|
+
# provide a name if the label is left blank
|
88
|
+
# if label.to_s == '$blank'
|
89
|
+
# hash[colourized_hash[label].name] = hash.delete(label)
|
90
|
+
# puts "Blank colour #{colour} has been given the name: #{colourized_hash[label].name}"
|
91
|
+
# end
|
92
|
+
rescue ColorConverters::InvalidColorError => e
|
93
|
+
warn "Error processing colour #{label} with value #{colour}: #{e.message}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
colourized_hash
|
98
|
+
end
|
99
|
+
|
100
|
+
# * Palettes
|
101
|
+
|
102
|
+
# ? Multiple Colour's Palette
|
103
|
+
# a function to loop through a map of colours and create absolute or relative palettes for them all and collect this into a single map
|
104
|
+
|
105
|
+
def self.palette_looper(colour_map, method)
|
106
|
+
looped_generated_palettes = {}
|
107
|
+
|
108
|
+
colour_map.each do |label, base_colour|
|
109
|
+
# create a palette for a single colour from the providing mapping
|
110
|
+
if method == 'absolutes'
|
111
|
+
generated_swatches = UtilityPalettes::Swatch.absolute_generator(label, base_colour, @config.dig(:method), @increment_steppers)
|
112
|
+
elsif method == 'relatives'
|
113
|
+
generated_swatches = UtilityPalettes::Swatch.relative_generator(label, base_colour, @config.dig(:method), @increment_steppers)
|
114
|
+
end
|
115
|
+
|
116
|
+
# merge the colours absolute palette into the collective mapping
|
117
|
+
looped_generated_palettes = {}.merge(looped_generated_palettes, generated_swatches)
|
118
|
+
end
|
119
|
+
|
120
|
+
looped_generated_palettes
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.format_palette(palettes)
|
124
|
+
palettes.compact_blank!
|
125
|
+
|
126
|
+
palettes.transform_keys!(&:to_s)
|
127
|
+
palettes.transform_keys! { |key| @config.dig(:output, :prefix) + key } if @config.dig(:output, :prefix).present?
|
128
|
+
palettes.transform_keys! { |key| key + @config.dig(:output, :suffix) } if @config.dig(:output, :suffix).present?
|
129
|
+
|
130
|
+
palettes
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'color_converters'
|
4
|
+
require 'bigdecimal'
|
5
|
+
|
6
|
+
# an increase in the 'level' makes the percieved colour darker
|
7
|
+
|
8
|
+
module UtilityPalettes
|
9
|
+
class Sequences
|
10
|
+
def self.hsl(colour, level_change, increment_steppers)
|
11
|
+
h_step = increment_steppers[:h_step]
|
12
|
+
s_step = increment_steppers[:s_step]
|
13
|
+
l_step = increment_steppers[:l_step]
|
14
|
+
|
15
|
+
# Use BigDecimal for precise decimal arithmetic
|
16
|
+
h_value = (BigDecimal(colour.hsl[:h].to_s) - BigDecimal((level_change * h_step).to_s)) % 360
|
17
|
+
s_value = (BigDecimal(colour.hsl[:s].to_s) - BigDecimal((level_change * s_step).to_s)).clamp(0, 100)
|
18
|
+
l_value = (BigDecimal(colour.hsl[:l].to_s) - BigDecimal((level_change * l_step).to_s)).clamp(0, 100)
|
19
|
+
|
20
|
+
ColorConverters::Color.new(h: h_value.to_f, s: s_value.to_f, l: l_value.to_f)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.rgb(colour, level_change, increment_steppers)
|
24
|
+
r_step = increment_steppers[:r_step] || increment_steppers[:rgb_step]
|
25
|
+
g_step = increment_steppers[:g_step] || increment_steppers[:rgb_step]
|
26
|
+
b_step = increment_steppers[:b_step] || increment_steppers[:rgb_step]
|
27
|
+
|
28
|
+
# Use BigDecimal for precise decimal arithmetic
|
29
|
+
r_value = (BigDecimal(colour.rgb[:r].to_s) - BigDecimal((level_change * r_step).to_s)).clamp(0, 255).to_f
|
30
|
+
g_value = (BigDecimal(colour.rgb[:g].to_s) - BigDecimal((level_change * g_step).to_s)).clamp(0, 255).to_f
|
31
|
+
b_value = (BigDecimal(colour.rgb[:b].to_s) - BigDecimal((level_change * b_step).to_s)).clamp(0, 255).to_f
|
32
|
+
|
33
|
+
ColorConverters::Color.new(r: r_value, g: g_value, b: b_value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def tailwind(_colour, _step, _go_lighter, _increment_steppers)
|
37
|
+
# def pSBC(p, _c0, _c1, l)
|
38
|
+
# r = nil
|
39
|
+
# g = nil
|
40
|
+
# b = nil
|
41
|
+
# pP = nil
|
42
|
+
# f = nil
|
43
|
+
# t = nil
|
44
|
+
|
45
|
+
# if l
|
46
|
+
# pPr = pP * f[:r]
|
47
|
+
# f[:g]
|
48
|
+
# pPb = pP * f[:b]
|
49
|
+
|
50
|
+
# ptr = p * t[:r]
|
51
|
+
# t[:g]
|
52
|
+
# ptb = p * t[:b]
|
53
|
+
|
54
|
+
# r = (pPr + ptr).round
|
55
|
+
# g = (pPb + ptb).round
|
56
|
+
# b = (pPb + ptb).round
|
57
|
+
# else
|
58
|
+
# pPr = pP * (f[:r]**2)
|
59
|
+
# pPg = pP * (f[:g]**2)
|
60
|
+
# pPb = pP * (f[:b]**2)
|
61
|
+
|
62
|
+
# ptr = p * (t[:r]**2)
|
63
|
+
# ptg = p * (t[:g]**2)
|
64
|
+
# ptb = p * (t[:b]**2)
|
65
|
+
|
66
|
+
# r = ((pPr + ptr)**0.5).round
|
67
|
+
# g = ((pPg + ptg)**0.5).round
|
68
|
+
# b = ((pPb + ptb)**0.5).round
|
69
|
+
# end
|
70
|
+
|
71
|
+
# a = f.a
|
72
|
+
# t = t.a
|
73
|
+
# f = a >= 0 || t >= 0
|
74
|
+
|
75
|
+
# t if f && a.negative?
|
76
|
+
|
77
|
+
# a = if f
|
78
|
+
# if a.negative?
|
79
|
+
# t
|
80
|
+
# else
|
81
|
+
# t.negative? ? a : a * pP + t * p
|
82
|
+
# end
|
83
|
+
# else
|
84
|
+
# 0
|
85
|
+
# end
|
86
|
+
|
87
|
+
ColorConverters::Color.new(r: r, g: g, b: b, a: m(a * 1000) / 1000)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UtilityPalettes
|
4
|
+
class Swatch
|
5
|
+
# ? Single Colour's Palette
|
6
|
+
# a function to create an absolute palette that incorporates a single colour input
|
7
|
+
def self.absolute_generator(label, base_colour, method, increment_steppers)
|
8
|
+
@method = method
|
9
|
+
@increment_steppers = increment_steppers
|
10
|
+
|
11
|
+
# colours are index inversely to their lightness
|
12
|
+
base_level = UtilityPalettes::Swatch.base_lightness_index(base_colour)
|
13
|
+
generated_absolute_swatches = {}.merge({ UtilityPalettes::Swatch.label(label, base_level) => base_colour })
|
14
|
+
|
15
|
+
# # Lighter colours
|
16
|
+
# # calc the space available to create lightened colours based off the base colour
|
17
|
+
# if base_level.positive?
|
18
|
+
# (0..base_level).each do |new_level|
|
19
|
+
# new_colour = UtilityPalettes::Swatch.generate(base_colour, base_level, new_level)
|
20
|
+
# generated_absolute_swatches = {}.merge(generated_absolute_swatches, { UtilityPalettes::Swatch.label(label, new_level) => new_colour })
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
|
24
|
+
# # Darker colours
|
25
|
+
# # calc the space available to create darkened colours based off the base colour
|
26
|
+
# if base_level < 9
|
27
|
+
# (base_level..9).each do |new_level|
|
28
|
+
# new_colour = UtilityPalettes::Swatch.generate(base_colour, base_level, new_level)
|
29
|
+
# generated_absolute_swatches = {}.merge(generated_absolute_swatches, { UtilityPalettes::Swatch.label(label, new_level) => new_colour })
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
|
33
|
+
if base_level.positive?
|
34
|
+
(0..9).each do |new_level|
|
35
|
+
new_colour = UtilityPalettes::Swatch.generate(base_colour, base_level, new_level)
|
36
|
+
generated_absolute_swatches = {}.merge(generated_absolute_swatches, { UtilityPalettes::Swatch.label(label, new_level) => new_colour })
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
generated_absolute_swatches
|
41
|
+
end
|
42
|
+
|
43
|
+
# ? Single Colour's Relative Palette
|
44
|
+
# a function to create a relative palette centred on a single colour input
|
45
|
+
def self.relative_generator(label, base_colour, method, increment_steppers)
|
46
|
+
@method = method
|
47
|
+
@increment_steppers = increment_steppers
|
48
|
+
|
49
|
+
lighter_colour = nil
|
50
|
+
darker_colour = nil
|
51
|
+
|
52
|
+
# colours are index inversely to their lightness
|
53
|
+
base_level = UtilityPalettes::Swatch.base_lightness_index(base_colour)
|
54
|
+
generated_relative_swatches = {}.merge({ label => base_colour })
|
55
|
+
|
56
|
+
# Lighter Colour
|
57
|
+
if base_level > 1
|
58
|
+
lighter_colour = UtilityPalettes::Swatch.generate(base_colour, base_level, base_level + 2)
|
59
|
+
elsif base_level > 0
|
60
|
+
lighter_colour = UtilityPalettes::Swatch.generate(base_colour, base_level, base_level + 1)
|
61
|
+
else
|
62
|
+
lighter_colour = nil
|
63
|
+
end
|
64
|
+
|
65
|
+
# Darker Colour
|
66
|
+
if base_level < 8
|
67
|
+
darker_colour = UtilityPalettes::Swatch.generate(base_colour, base_level, base_level - 2)
|
68
|
+
elsif base_level < 9
|
69
|
+
darker_colour = UtilityPalettes::Swatch.generate(base_colour, base_level, base_level - 1)
|
70
|
+
else
|
71
|
+
darker_colour = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
generated_relative_swatches = {}.merge(generated_relative_swatches, { "#{label}-light" => lighter_colour })
|
75
|
+
generated_relative_swatches = {}.merge(generated_relative_swatches, { "#{label}-dark" => darker_colour })
|
76
|
+
|
77
|
+
generated_relative_swatches
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.base_lightness_index(colour)
|
81
|
+
9 - (colour.hsl[:l] / 10).floor
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.label(label, index)
|
85
|
+
levels = { '0' => 50, '1' => 100, '2' => 200, '3' => 300, '4' => 400, '5' => 500, '6' => 600, '7' => 700, '8' => 800, '9' => 900 }
|
86
|
+
|
87
|
+
[label, levels.dig(index.to_s)].join('-')
|
88
|
+
end
|
89
|
+
|
90
|
+
# TODO: create other sequence methods
|
91
|
+
# ? How to Calculate the next colour in the Palette
|
92
|
+
def self.generate(colour, base_level, new_level)
|
93
|
+
case @method
|
94
|
+
when 'hsl'
|
95
|
+
UtilityPalettes::Sequences.hsl(colour, new_level - base_level, @increment_steppers)
|
96
|
+
when 'rgb'
|
97
|
+
# TODO
|
98
|
+
else
|
99
|
+
UtilityPalettes::Sequences.hsl(colour, new_level - base_level, @increment_steppers)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_step_check
|
104
|
+
# const ALL_LEVELS = [50, 100, 200, 300, 400, 600, 700, 800, 900];
|
105
|
+
# const levels = options.levels == null ? ALL_LEVELS : options.levels.filter(level => ALL_LEVELS.includes(level));
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UtilityPalettes
|
4
|
+
class Validations
|
5
|
+
def self.validate_config(config)
|
6
|
+
sequence_options = ['hsl']
|
7
|
+
warn "ERROR: The colour sequence method you have submitted to Utility Palettes < #{config[:steps]} > is not available (#{sequence_options.to_sentence})" if !config[:method].nil? && !config[:method].to_s.in?(sequence_options)
|
8
|
+
|
9
|
+
warn "ERROR: The colour sequence steps you have submitted to Utility Palettes < #{config[:steps]} > have not been formatted as a hash" if !config[:steps].nil? && !config[:steps].is_a?(Hash)
|
10
|
+
|
11
|
+
warn "ERROR: The absolute swatches you have submitted to Utility Palettes < #{config[:absolutes]} > have not been formatted as a hash" if !config[:absolutes].nil? && !config[:absolutes].is_a?(Hash)
|
12
|
+
|
13
|
+
warn "ERROR: The relative swatches you have submitted to Utility Palettes < #{config[:relatives]} > have not been formatted as a hash" if !config[:relatives].nil? && !config[:relatives].is_a?(Hash)
|
14
|
+
|
15
|
+
warn "ERROR: The single swatches you have submitted to Utility Palettes < #{config[:singles]} > have not been formatted as a hash" if !config[:singles].nil? && !config[:singles].is_a?(Hash)
|
16
|
+
|
17
|
+
config_keys = config.keys - [:defaults, :output, :method, :steps, :absolutes, :relatives, :singles, :disabled]
|
18
|
+
|
19
|
+
warn "WARNING: Utility Palettes does not recognize the following keys; #{config_keys.join(', ')}. Please check they match the spelling of documented palette keys in order for them to be used." if config_keys.present?
|
20
|
+
|
21
|
+
true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'utility_palettes/version'
|
4
|
+
|
5
|
+
require_relative 'utility_palettes/configuration'
|
6
|
+
require_relative 'utility_palettes/defaults'
|
7
|
+
require_relative 'utility_palettes/outputs'
|
8
|
+
require_relative 'utility_palettes/palettes'
|
9
|
+
require_relative 'utility_palettes/sequences'
|
10
|
+
require_relative 'utility_palettes/swatch'
|
11
|
+
require_relative 'utility_palettes/validations'
|
12
|
+
|
13
|
+
module UtilityPalettes
|
14
|
+
class Error < StandardError; end
|
15
|
+
# Your code goes here...
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: utility_palettes
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Louis Davis
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-08-08 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: color_converters
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: 0.1.2
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 0.1.2
|
26
|
+
description: Create broad colour palettes based on specific colour swatches, or more
|
27
|
+
specific light/dark variant palettes. Be in control of the colours you use and shorten
|
28
|
+
the iterative process when deciding on how to build your palettes.
|
29
|
+
email:
|
30
|
+
- LouisWilliamDavis@gmail.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- README.md
|
36
|
+
- Rakefile
|
37
|
+
- lib/generators/templates/config/utility_palettes.yml
|
38
|
+
- lib/generators/utility_palettes/config_generator.rb
|
39
|
+
- lib/generators/utility_palettes/generate_generator.rb
|
40
|
+
- lib/utility_palettes.rb
|
41
|
+
- lib/utility_palettes/configuration.rb
|
42
|
+
- lib/utility_palettes/defaults.rb
|
43
|
+
- lib/utility_palettes/outputs.rb
|
44
|
+
- lib/utility_palettes/palettes.rb
|
45
|
+
- lib/utility_palettes/sequences.rb
|
46
|
+
- lib/utility_palettes/swatch.rb
|
47
|
+
- lib/utility_palettes/validations.rb
|
48
|
+
- lib/utility_palettes/version.rb
|
49
|
+
homepage: https://github.com/louiswdavis/utility_palettes
|
50
|
+
licenses:
|
51
|
+
- MIT
|
52
|
+
metadata:
|
53
|
+
homepage_uri: https://github.com/louiswdavis/utility_palettes
|
54
|
+
source_code_uri: https://github.com/louiswdavis/utility_palettes
|
55
|
+
changelog_uri: https://github.com/louiswdavis/utility_palettes/blob/master/CHANGELOG.md
|
56
|
+
rdoc_options: []
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 1.9.3
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
requirements: []
|
70
|
+
rubygems_version: 3.6.3
|
71
|
+
specification_version: 4
|
72
|
+
summary: Quickly build colour palettes based on default or supplied colour swatches,
|
73
|
+
and export them for use in stylesheets.
|
74
|
+
test_files: []
|