@financial-times/o-private-foundation 1.0.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. package/README.md +108 -0
  2. package/demos/src/demo.mustache +0 -0
  3. package/demos/src/demo.scss +1 -0
  4. package/main.js +60 -0
  5. package/main.scss +32 -0
  6. package/origami.json +26 -0
  7. package/package.json +33 -0
  8. package/src/scss/_brand.scss +68 -0
  9. package/src/scss/_tokens.scss +15 -0
  10. package/src/scss/_variables.scss +1 -0
  11. package/src/scss/o-buttons/custom-themes.scss +631 -0
  12. package/src/scss/o-buttons/main.scss +255 -0
  13. package/src/scss/o-colors/_functions.scss +354 -0
  14. package/src/scss/o-colors/_palette.scss +2 -0
  15. package/src/scss/o-colors/_variables.scss +50 -0
  16. package/src/scss/o-colors/main.scss +6 -0
  17. package/src/scss/o-grid/_functions.scss +90 -0
  18. package/src/scss/o-grid/_mixins.scss +384 -0
  19. package/src/scss/o-grid/_variables.scss +105 -0
  20. package/src/scss/o-grid/main.scss +5 -0
  21. package/src/scss/o-icons/_mixins.scss +71 -0
  22. package/src/scss/o-icons/main.scss +1 -0
  23. package/src/scss/o-normalise/_mixins.scss +126 -0
  24. package/src/scss/o-normalise/_variables.scss +10 -0
  25. package/src/scss/o-normalise/main.scss +4 -0
  26. package/src/scss/o-spacing/_variables.scss +29 -0
  27. package/src/scss/o-spacing/main.scss +28 -0
  28. package/src/scss/o-typography/main.scss +428 -0
  29. package/src/scss/o-visual-effects/main.scss +4 -0
  30. package/src/scss/o-visual-effects/scss/_shadows.scss +31 -0
  31. package/src/scss/o-visual-effects/scss/_variables.scss +13 -0
  32. package/src/scss/tokens/core.scss +563 -0
  33. package/src/scss/tokens/internal.scss +456 -0
  34. package/src/scss/tokens/professional.scss +537 -0
  35. package/src/scss/tokens/sustainable-views.scss +493 -0
  36. package/src/scss/tokens/whitelabel.scss +465 -0
@@ -0,0 +1,255 @@
1
+ @import './custom-themes';
2
+
3
+ /// Create a single button with a custom class.
4
+ /// @param {Map} $opts [('type': 'null', 'theme': null, 'size': null, 'icon': null, 'icon-only': false)] - The kind of button styles to output.
5
+ @mixin oPrivateButtonsContent(
6
+ $opts: (
7
+ 'type': null,
8
+ 'theme': null,
9
+ 'size': null,
10
+ 'icon': null,
11
+ 'icon-only': false,
12
+ )
13
+ ) {
14
+ $theme: map-get($opts, 'theme');
15
+ $theme: if($theme, $theme, 'standard');
16
+ $type: map-get($opts, 'type');
17
+ $type: if($type, $type, 'primary');
18
+
19
+ @include _oPrivateButtonsBase($opts);
20
+
21
+ /*
22
+ Type and theme specific styles.
23
+ FT Professional was treated as a theme, but is now considered a brand in tokens.
24
+ */
25
+ $sub-brand: null;
26
+ @if $theme == 'professional' {
27
+ $theme: 'standard';
28
+ $sub-brand: 'professional';
29
+ }
30
+
31
+ @if $theme == 'professional-inverse' {
32
+ $theme: 'inverse';
33
+ $sub-brand: 'professional';
34
+ }
35
+ @include _oPrivateButtonsTheme(
36
+ (
37
+ 'color':
38
+ oPrivateFoundationGet(
39
+ '_o3-button-' + $type + '-' + $theme + '-color',
40
+ $sub-brand
41
+ ),
42
+ 'background':
43
+ oPrivateFoundationGet(
44
+ '_o3-button-' + $type + '-' + $theme + '-background',
45
+ $sub-brand
46
+ ),
47
+ 'border':
48
+ oPrivateFoundationGet(
49
+ '_o3-button-' + $type + '-' + $theme + '-border',
50
+ $sub-brand
51
+ ),
52
+ 'hover-color':
53
+ oPrivateFoundationGet(
54
+ '_o3-button-' + $type + '-' + $theme + '-hover-color',
55
+ $sub-brand
56
+ ),
57
+ 'hover-background':
58
+ oPrivateFoundationGet(
59
+ '_o3-button-' + $type + '-' + $theme + '-hover-background',
60
+ $sub-brand
61
+ ),
62
+ 'hover-border':
63
+ oPrivateFoundationGet(
64
+ '_o3-button-' + $type + '-' + $theme + '-hover-border',
65
+ $sub-brand
66
+ ),
67
+ 'focus-color':
68
+ oPrivateFoundationGet(
69
+ '_o3-button-' + $type + '-' + $theme + '-focus-color',
70
+ $sub-brand
71
+ ),
72
+ 'focus-background':
73
+ oPrivateFoundationGet(
74
+ '_o3-button-' + $type + '-' + $theme + '-focus-background',
75
+ $sub-brand
76
+ ),
77
+ 'focus-border':
78
+ oPrivateFoundationGet(
79
+ '_o3-button-' + $type + '-' + $theme + '-focus-border',
80
+ $sub-brand
81
+ ),
82
+ 'active-color':
83
+ oPrivateFoundationGet(
84
+ '_o3-button-' + $type + '-' + $theme + '-active-color',
85
+ $sub-brand
86
+ ),
87
+ 'active-background':
88
+ oPrivateFoundationGet(
89
+ '_o3-button-' + $type + '-' + $theme + '-active-background',
90
+ $sub-brand
91
+ ),
92
+ 'active-border':
93
+ oPrivateFoundationGet(
94
+ '_o3-button-' + $type + '-' + $theme + '-active-border',
95
+ $sub-brand
96
+ ),
97
+ )
98
+ );
99
+ }
100
+
101
+ // Button styles shared across all types/theme.
102
+ @mixin _oPrivateButtonsBase(
103
+ $opts: (
104
+ 'size': null,
105
+ 'icon': null,
106
+ 'icon-only': false,
107
+ )
108
+ ) {
109
+ $icon-only: map-get($opts, 'icon-only');
110
+ $size: map-get($opts, 'size');
111
+ $icon: map-get($opts, 'icon');
112
+
113
+ font-family: oPrivateFoundationGet('o3-type-body-highlight-font-family');
114
+ font-size: oPrivateFoundationGet('o3-type-body-highlight-font-size');
115
+ line-height: oPrivateFoundationGet('o3-type-body-highlight-line-height');
116
+ font-weight: oPrivateFoundationGet('o3-type-body-highlight-font-weight');
117
+
118
+ --_o-pf-button-border-size: 1px;
119
+ --_o-pf-button-icon-size: calc(
120
+ var(--_o-pf-button-min-height) - (var(--_o-pf-button-block-padding) * 2)
121
+ );
122
+
123
+ /* Button Sizes */
124
+ @if ($size == 'big') {
125
+ --_o-pf-button-min-width: 80px;
126
+ --_o-pf-button-min-height: 44px;
127
+
128
+ --_o-pf-button-inline-padding-start: #{oPrivateFoundationGet(
129
+ 'o3-spacing-2xs'
130
+ )};
131
+ --_o-pf-button-inline-padding-end: #{oPrivateFoundationGet(
132
+ 'o3-spacing-2xs'
133
+ )};
134
+ --_o-pf-button-block-padding: #{oPrivateFoundationGet('o3-spacing-4xs')};
135
+ } @else {
136
+ --_o-pf-button-min-width: 60px;
137
+ --_o-pf-button-min-height: 28px;
138
+
139
+ --_o-pf-button-inline-padding-start: #{oPrivateFoundationGet(
140
+ 'o3-spacing-4xs'
141
+ )};
142
+ --_o-pf-button-inline-padding-end: #{oPrivateFoundationGet(
143
+ 'o3-spacing-4xs'
144
+ )};
145
+ --_o-pf-button-block-padding: #{oPrivateFoundationGet('o3-spacing-5xs')};
146
+ // Non-standard font size for small buttons.
147
+ font-size: oPrivateFoundationGet('o3-font-size-negative-2');
148
+ line-height: oPrivateFoundationGet('o3-font-lineheight-negative-2');
149
+ }
150
+
151
+ min-width: var(--_o-pf-button-min-width);
152
+ min-height: var(--_o-pf-button-min-height, 44px);
153
+ padding: 0 var(--_o-pf-button-inline-padding-end) 0
154
+ var(--_o-pf-button-inline-padding-start);
155
+ display: inline-flex;
156
+ align-items: center;
157
+ justify-content: center;
158
+ box-sizing: border-box;
159
+ vertical-align: middle;
160
+ border: var(--_o-pf-button-border-size) solid transparent;
161
+ border-radius: oPrivateFoundationGet('o3-border-radius-1');
162
+ text-align: center;
163
+ text-decoration: none;
164
+ cursor: pointer;
165
+ transition: 0.3s background-color, 0.15s color ease-out,
166
+ 0.15s border-color ease-out;
167
+ appearance: none;
168
+
169
+ &[disabled] {
170
+ pointer-events: none;
171
+ opacity: 0.4;
172
+ cursor: default;
173
+ }
174
+
175
+ /* ICON */
176
+ @if ($icon) {
177
+ --_o-pf-button-inline-padding-end: #{oPrivateFoundationGet('o3-spacing-s')};
178
+ @if ($size != 'big') {
179
+ --_o-pf-button-inline-padding-end: #{oPrivateFoundationGet(
180
+ 'o3-spacing-4xs'
181
+ )};
182
+ }
183
+
184
+ &::before {
185
+ content: '';
186
+ width: var(--_o-pf-button-icon-size);
187
+ height: var(--_o-pf-button-icon-size);
188
+ mask-image: #{oPrivateFoundationGet('o3-icon-' + $icon)};
189
+ mask-repeat: no-repeat;
190
+ mask-size: contain;
191
+ display: inline-block;
192
+ background-color: currentColor;
193
+ margin-right: #{oPrivateFoundationGet('o3-spacing-4xs')};
194
+ }
195
+
196
+ /*
197
+ Ensure iconography is maintained when forced colours is enabled.
198
+ E.g. Windows High Contrast Mode
199
+ https://drafts.csswg.org/css-color/#valdef-color-buttontext
200
+ */
201
+ @media (forced-colors: active) {
202
+ &:is(button)::before {
203
+ background-color: ButtonText;
204
+ }
205
+ &:is(a)::before {
206
+ background-color: LinkText;
207
+ }
208
+ }
209
+ }
210
+
211
+ // ICON ONLY
212
+ @if ($icon-only) {
213
+ --_o-pf-button-min-width: var(--_o-pf-button-min-height);
214
+ --_o-pf-button-inline-padding-start: 0;
215
+ --_o-pf-button-inline-padding-end: 0;
216
+ &::before {
217
+ margin-right: 0;
218
+ }
219
+ }
220
+ }
221
+
222
+ // Button styles unique to a theme.
223
+ @mixin _oPrivateButtonsTheme($theme) {
224
+ color: map-get($theme, 'color');
225
+ background-color: map-get($theme, 'background');
226
+ border-color: map-get($theme, 'border');
227
+
228
+ &:hover {
229
+ color: map-get($theme, 'hover-color');
230
+ background-color: map-get($theme, 'hover-background');
231
+ border-color: map-get($theme, 'hover-border');
232
+ }
233
+
234
+ &:focus-visible {
235
+ outline: 0; // Undo standard o2 focus styles
236
+ color: map-get($theme, 'focus-color');
237
+ background-color: map-get($theme, 'focus-background');
238
+ border-color: map-get($theme, 'focus-border');
239
+ }
240
+
241
+ @include oPrivateNormaliseFocusApply() {
242
+ @include oPrivateNormaliseFocusContentForElementColour(
243
+ map-get($theme, 'focus-background')
244
+ );
245
+ // Ensure that the focus ring of a button is not hidden by
246
+ // adjacent buttons, e.g. in tabs or pagination contexts.
247
+ z-index: 1;
248
+ }
249
+
250
+ &:is(:active, [aria-selected='true'], [aria-current], [aria-pressed='true']) {
251
+ color: map-get($theme, 'active-color');
252
+ background-color: map-get($theme, 'active-background');
253
+ border-color: map-get($theme, 'active-border');
254
+ }
255
+ }
@@ -0,0 +1,354 @@
1
+ /// Returns a brighter or darker tone of a colour, where the hue remains
2
+ /// the same but the saturation and luminance changes.
3
+ ///
4
+ /// Not all our colours allow tones. If a colour cannot be toned an error is
5
+ /// thrown. You may however mix the colour with another supported colour.
6
+ /// @see oPrivateColorsMix
7
+ ///
8
+ /// @param {String} $color-name - the name of the color to be shaded
9
+ /// @param {Number} $brightness - the brightness value of the new color, 0-100
10
+ @function oPrivateColorsGetTone($color-name, $brightness) {
11
+ // Find palette colour information.
12
+ $color: oPrivateFoundationGet($color-name);
13
+ $color-map: map-get($_o-pf-colors-palette, $color-name);
14
+ $color-meta: map-get($color-map, 'meta');
15
+
16
+ // Validate brightness.
17
+ @if(type-of($brightness) != 'number' or $brightness > 100 or $brightness < 0) {
18
+ @return _oPrivateColorsError('"$brightness" must be a number between 0 and 100.');
19
+ }
20
+
21
+ // Error for colours which have no hue, such as white and black, which we do not allow tones of.
22
+ @if (hue($color) == 0 and saturation($color) == 0) {
23
+ @return _oPrivateColorsError('"#{$color-name}" does not support tones. ' +
24
+ 'Use a mix instead: ' +
25
+ '`oPrivateColorsMix(\'#{$color-name}\', $percentage: #{$brightness})`');
26
+ }
27
+
28
+ // Error for any palette colour which hasn't been configured to allow tones.
29
+ $allows-tones: map-get($color-meta, 'allow-tones');
30
+ @if (not $allows-tones) {
31
+ @return _oPrivateColorsError('"#{$color-name}" does not allow tones. ' +
32
+ 'We only allow tones for some colours, to reduce the number ' +
33
+ 'of different colours used across sites. ' +
34
+ 'For custom colours, set the `allow-tones` option ' +
35
+ 'of `oPrivateColorsSetColor` to enable tones. ' +
36
+ 'If using a default o-colors colour consider using the `oPrivateColorsMix` ' +
37
+ 'function to mix with black to darken or white to lighten.');
38
+ }
39
+
40
+ // Convert the given colour to the HSB colour space.
41
+ $hsb: _oPrivateColorsHexToHsbValues($color);
42
+ $hsb-hue: map-get($hsb, 'h');
43
+ $hsb-saturation: map-get($hsb, 's');
44
+ // Update the colours brightness with the given brightness value,
45
+ // using the HSB colour space.
46
+ @return _oPrivateColorsHsbToHex($hsb-hue, $hsb-saturation, $brightness);
47
+ }
48
+
49
+ /// Figure out if a given colour is a tone. If it is a tone return the original
50
+ /// colour name and its tone brightness, otherwise return null.
51
+ ///
52
+ /// @example Get the tone brightness of 'claret-80'
53
+ /// $tone-details: oPrivateColorsGetToneDetails('claret-80');
54
+ /// $color-name: map-get($tone-details, 'color-name'); // claret
55
+ /// $brightness: map-get($tone-details, 'brightness'); // 80
56
+ ///
57
+ /// @param {String} $color - the palette colour or color name e.g. 'claret-80'
58
+ /// @return {Map|Null} - the details of the given tone e.g. ('color-name': 'claret', 'brightness': 80) )
59
+ @function oPrivateColorsGetToneDetails($color) {
60
+ $color: if(type-of($color) == 'string', oPrivateFoundationGet($color), $color);
61
+ $hue: hue($color);
62
+ @each $tone-color, $tone-config in $_o-pf-colors-default-palette-tones {
63
+ // Check the given colour against the tone to find the tone brightness.
64
+ $brightness: 0;
65
+ @while $brightness <= 100 {
66
+ @if(inspect(oPrivateColorsGetTone($tone-color, $brightness)) == inspect($color)) {
67
+ @return (
68
+ 'color-name': $tone-color,
69
+ 'brightness': $brightness
70
+ );
71
+ }
72
+ $brightness: $brightness + 1;
73
+ }
74
+ }
75
+ // No tone matched.
76
+ @return null;
77
+ }
78
+
79
+ /// Returns a color based on the background context and base color
80
+ /// at the supplied percentage
81
+ ///
82
+ /// @param {String|Color} $color [black] - palette name of color
83
+ /// @param {String|Color} $background [paper] - palette name of background color
84
+ /// @param {Number} $percentage [60] - percentage opacity of the foreground color over the background
85
+ @function oPrivateColorsMix($color: 'o3-color-palette-black', $background: oPrivateFoundationGet('o3-color-use-case-page-background'), $percentage: 80) {
86
+ // Cast colour arguments to string before checking if they exist in our pallet.
87
+ // If colour names are passed without quotes they will be of type `color`.
88
+ // We want both of these to be equivalent:
89
+ // oPrivateColorsMix('o3-color-palette-paper', 'o3-color-palette-wheat', 30);
90
+ // oPrivateColorsMix(o3-color-palette-paper, o3-color-palette-wheat, 30);
91
+ // https://www.w3.org/wiki/CSS/Properties/color/keywords
92
+ $base: if(type-of($background) == color, $background, oPrivateFoundationGet(#{$background}));
93
+ $mixer: if(type-of($color) == "color", $color, oPrivateFoundationGet(#{$color}));
94
+
95
+ @if type-of($base) != color {
96
+ @return _oPrivateColorsError("'#{inspect($background)}' is not a valid base color.");
97
+ }
98
+
99
+ @if type-of($mixer) != color {
100
+ @return _oPrivateColorsError("'#{inspect($color)}' is not a valid mixing color.");
101
+ }
102
+
103
+ @if (unitless($percentage)) {
104
+ @return mix($mixer, $base, $percentage * 1%);
105
+ }
106
+
107
+ @return mix($mixer, $base, $percentage);
108
+ }
109
+
110
+ /// Returns a text color based on the background and
111
+ /// an opacity percentage the color should appear at
112
+ ///
113
+ /// @param {Color|String} $background - the color or palette color name of the background the text will appear on
114
+ /// @param {Number} $opacity [100] - the opacity percentage the text color should appear at
115
+ /// @param {String|Null} $minimum-contrast ['aa-normal'] - the minimum contrast ratio standard between the background and the returned text color, one of: aa-normal, aa-large, aaa-normal, aaa-large. See [WCAG 2.1 guidelines](https://www.w3.org/TR/WCAG21/#contrast-minimum). If the contrast ratio is too low to meet the selected guideline an error is thrown. Set to `null` to remove contrast checking and never throw an error.
116
+ @function oPrivateColorsGetTextColor($background, $opacity: 90, $minimum-contrast: 'aa-normal') {
117
+ $background-name: $background;
118
+ // Get background color if palette colour name has been given.
119
+ $background: if(type-of($background) == 'string', oPrivateFoundationGet($background), $background);
120
+
121
+ // Contrast values. See https://www.w3.org/TR/WCAG21/#contrast-minimum
122
+ $contrast-levels: (
123
+ 'aa-normal': 4.5,
124
+ 'aa-large': 3,
125
+ 'aaa-normal': 7,
126
+ 'aaa-large': 4.5
127
+ );
128
+
129
+ // Validate arguments.
130
+ @if($minimum-contrast != null and not map-has-key($contrast-levels, $minimum-contrast)) {
131
+ @return _oPrivateColorsError('The minimum contrast must by one of "#{map-keys($contrast-levels)}" '+
132
+ 'or `null`. Found "#{inspect($minimum-contrast)}".');
133
+ }
134
+
135
+ @if type-of($background) != color {
136
+ @return _oPrivateColorsError("'#{inspect($background)}' is not a valid color. To get a text color, please supply a valid color or palette color name for the background color'");
137
+ }
138
+
139
+ @if type-of($opacity) != 'number' {
140
+ @return _oPrivateColorsError("'#{inspect($opacity)}' is not a valid opacity, set to a number.'");
141
+ }
142
+
143
+ $contrast-ratio-aim: map-get($contrast-levels, if($minimum-contrast, $minimum-contrast, 'aa-normal'));
144
+
145
+ // Calculate text colour for background and opacity.
146
+ $base-color-a: if(oPrivateColorsColorBrightness($background) < 65%, 'o3-color-palette-white', 'o3-color-palette-black');
147
+ $text-color-a: oPrivateColorsMix($base-color-a, $background, $opacity);
148
+ $contrast-ratio-a: oPrivateColorsGetContrastRatio($text-color-a, $background);
149
+ @if $contrast-ratio-a > $contrast-ratio-aim {
150
+ @return $text-color-a;
151
+ }
152
+
153
+ // Switch the base colour if the first attempt did not pass contrast checks.
154
+ $base-color-b: if($base-color-a == 'o3-color-palette-black', 'o3-color-palette-white', 'o3-color-palette-black');
155
+ $text-color-b: oPrivateColorsMix($base-color-b, $background, $opacity);
156
+ $contrast-ratio-b: oPrivateColorsGetContrastRatio($text-color-b, $background);
157
+ @if $contrast-ratio-b > $contrast-ratio-aim {
158
+ @return $text-color-b;
159
+ }
160
+
161
+ // Error if neither base colour produced a text colour of high enough contrast.
162
+ @if $minimum-contrast != null {
163
+ $best-contrast-ratio: if($contrast-ratio-a > $contrast-ratio-b, $contrast-ratio-a, $contrast-ratio-b);
164
+ @return _oPrivateColorsError(
165
+ 'The text colour generated for #{inspect($background-name)} at ' +
166
+ '#{inspect($opacity)}% opacity has a contrast ratio of ' +
167
+ '"#{inspect($best-contrast-ratio)}" and does not pass the WCAG 2.1 ' +
168
+ '#{$minimum-contrast} required contrast ratio of at least ' +
169
+ '#{$contrast-ratio-aim}:1. Update the `$minimum-contrast` argument ' +
170
+ 'if a lower contrast is acceptable.'
171
+ );
172
+ }
173
+
174
+ @return if($contrast-ratio-a > $contrast-ratio-b, $text-color-a, $text-color-b);
175
+ }
176
+
177
+ /// Work out the brightness value in % of a color
178
+ /// From: https://gist.github.com/jlong/f06f5843104ee10006fe
179
+ ///
180
+ /// @param {Color} $color - color value to get brightness from (either a CSS colour or o-colors palette colour name)
181
+ @function oPrivateColorsColorBrightness($color) {
182
+ $color: if(type-of($color) == 'string', oPrivateFoundationGet($color), $color);
183
+
184
+ $red-magic-number: 241;
185
+ $green-magic-number: 691;
186
+ $blue-magic-number: 68;
187
+
188
+ $brightness-divisor: $red-magic-number + $green-magic-number + $blue-magic-number;
189
+
190
+ // Extract color components
191
+ $red-component: red($color);
192
+ $green-component: green($color);
193
+ $blue-component: blue($color);
194
+
195
+ // Calculate a brightness value in 3d color space between 0 and 255
196
+ $number: sqrt(div(($red-component * $red-component * $red-magic-number) + ($green-component * $green-component * $green-magic-number) + ($blue-component * $blue-component * $blue-magic-number), $brightness-divisor));
197
+
198
+ // Convert to percentage and return
199
+ @return 100% * div($number, 255);
200
+ }
201
+
202
+ /// Returns the luminance of `$color` as a float (between 0 and 1)
203
+ /// 1 is pure white, 0 is pure black.
204
+ /// From: https://css-tricks.com/snippets/sass/luminance-color-function/
205
+ /// @param {String|Color} $color - The colour to return a luminance for (either a CSS colour or o-colors palette colour name)
206
+ /// @return {Number} - a number between 0 and 1
207
+ @function oPrivateColorsColorLuminance($color) {
208
+ $color: if(type-of($color) == 'string', oPrivateFoundationGet($color), $color);
209
+
210
+ $colors: (
211
+ 'red': red($color),
212
+ 'green': green($color),
213
+ 'blue': blue($color)
214
+ );
215
+
216
+ @each $name, $value in $colors {
217
+ $adjusted: 0;
218
+ $value: div($value, 255);
219
+
220
+ @if $value < 0.03928 {
221
+ $value: div($value, 12.92);
222
+ } @else {
223
+ $value: div($value + 0.055, 1.055);
224
+ $value: pow($value, 2.4);
225
+ }
226
+
227
+ $colors: map-merge($colors, ($name: $value));
228
+ }
229
+
230
+ @return (map-get($colors, 'red') * 0.2126) + (map-get($colors, 'green') * 0.7152) + (map-get($colors, 'blue') * 0.0722);
231
+ }
232
+
233
+ /// Calculate the contrast ratio between two colours.
234
+ ///
235
+ /// @param {String|Color} $color-a - first colour to compare (either a CSS colour or o-colors palette colour name)
236
+ /// @param {String|Color} $color-b - second colour to compare (either a CSS colour or o-colors palette colour name)
237
+ /// Based on the JS in https://github.com/LeaVerou/contrast-ratio/blob/gh-pages/contrast-ratio.js
238
+ @function oPrivateColorsGetContrastRatio($color-a, $color-b) {
239
+ $l1: oPrivateColorsColorLuminance($color-a) + 0.05;
240
+ $l2: oPrivateColorsColorLuminance($color-b) + 0.05;
241
+
242
+ $ratio: div($l1, $l2);
243
+
244
+ @if $l2 > $l1 {
245
+ $ratio: div(1, $ratio);
246
+ }
247
+
248
+ $ratio: _oPrivateColorsPreciseFloor($ratio);
249
+
250
+ @return $ratio;
251
+ }
252
+
253
+ /// @access private
254
+ @function _oPrivateColorsPreciseFloor($number, $decimals: 2) {
255
+ $multiplier: pow(10, $decimals);
256
+ @return div(floor($number * $multiplier), $multiplier);
257
+ }
258
+
259
+ /// Allows for errors to be tested in dev environments
260
+ /// Code from: https://github.com/oddbird/true/issues/92
261
+ /// @access private
262
+ @function _oPrivateColorsError($message, $capture: $_o-pf-colors-test-environment) {
263
+ @if $capture {
264
+ @return 'ERROR: #{$message}';
265
+ }
266
+
267
+ @error('#{$message}');
268
+ }
269
+
270
+ /// Get a namespace from a colour name.
271
+ /// Returns null if there is no namespace.
272
+ /// @example
273
+ /// $namespace: _oPrivateColorsGetNameSpace('o-example/paper'); // o-example
274
+ /// $namespace: _oPrivateColorsGetNameSpace('o-colors/paper'); // o-colors
275
+ /// $namespace: _oPrivateColorsGetNameSpace('paper'); // null
276
+ @function _oPrivateColorsGetNameSpace($color-name) {
277
+ $slash-index: str-index($color-name, '/');
278
+ @return if($slash-index, str-slice($color-name, 0, $slash-index - 1), null);
279
+ }
280
+
281
+ /// Add the default o-colors namespace if a namespace isn't given.
282
+ /// @example
283
+ /// $namespace: _oPrivateColorsRemoveDefaultNamespace('o-example/paper'); // o-example/paper
284
+ /// $namespace: _oPrivateColorsRemoveDefaultNamespace('o-colors/paper'); // paper
285
+ /// $namespace: _oPrivateColorsRemoveDefaultNamespace('paper'); // paper
286
+ @function _oPrivateColorsRemoveDefaultNamespace($color-name) {
287
+ $namespace: _oPrivateColorsGetNameSpace($color-name);
288
+ $slash-index: str-index($color-name, '/');
289
+ $color-part: if($namespace, str-slice($color-name, $slash-index + 1, -1), $color-name);
290
+ @return if($namespace == 'o-colors', $color-part, $color-name);
291
+ }
292
+
293
+ /// Check if a colour name exists in the palette.
294
+ @function _oPrivateColorsNameExists($color-name) {
295
+ @return map-has-key($_o-pf-colors-palette, $color-name);
296
+ }
297
+
298
+ /// Converts HSB/HSV values to a hex colour
299
+ ///
300
+ /// @access private
301
+ ///
302
+ /// @param {Number} $hue - number between 0-360
303
+ /// @param {Number} $saturation - number between 0-100
304
+ /// @param {Number} $brigthness - number between 0-100
305
+ ///
306
+ /// @return {Colour} - the hex representation of given hsb values
307
+ @function _oPrivateColorsHsbToHex($hue, $saturation, $brightness) {
308
+ @if $brightness == 0 {
309
+ @return hsl(0, 0%, 0%);
310
+ }
311
+
312
+ $hsl-luminance: (div($brightness, 2)) * (2 - (div($saturation, 100)));
313
+ $hsl-saturation: div($brightness * $saturation, if($hsl-luminance < 50, $hsl-luminance * 2, 200 - $hsl-luminance * 2));
314
+ @return hsl($hue, $hsl-saturation * 1%, $hsl-luminance * 1%);
315
+ }
316
+
317
+ /// Returns HSB/HSV colour values for a given colour hex.
318
+ ///
319
+ /// Logic derived from chroma.js
320
+ /// https://github.com/gka/chroma.js/blob/088e18f50a3b5b1d009ce68f540265cafa0cb6a1/src/io/hsv/rgb2hsv.js#L10
321
+ /// HSL to HSB is also documented here, if you prefer math:
322
+ /// https://www.rapidtables.com/convert/color/rgb-to-hsv.html
323
+ ///
324
+ /// @access private
325
+ ///
326
+ /// @param {Color} $hex - The hex colour value to find hsb values for.
327
+ /// @return {Map} - map of `h`, `s`, `b` colour values.
328
+ @function _oPrivateColorsHexToHsbValues($hex) {
329
+ $redValue: red($hex);
330
+ $greenValue: green($hex);
331
+ $blueValue: blue($hex);
332
+
333
+ // Find smallest and largest amount of colour.
334
+ $min: null;
335
+ $max: null;
336
+ @each $var in ($redValue, $greenValue, $blueValue) {
337
+ @if $min == null or $var < $min {
338
+ $min: $var;
339
+ }
340
+
341
+ @if $max == null or $var > $max {
342
+ $max: $var;
343
+ }
344
+ }
345
+
346
+ // Difference between the smallest and largest amount of colour.
347
+ $delta: $max - $min;
348
+
349
+ $hue: hue($hex);
350
+ $saturation: if($max == 0, 0, div($delta, $max));
351
+ $brightness: div($max, 255);
352
+
353
+ @return ('h': $hue, 's': $saturation * 100, 'b': $brightness * 100);
354
+ }
@@ -0,0 +1,2 @@
1
+
2
+ $_o-pf-colors-default-palette-colors: null;
@@ -0,0 +1,50 @@
1
+ /// A map to define default palette colours:
2
+ /// A list of arguments for `oPrivateColorsSetColor`, except the project name.
3
+ ///
4
+ /// @see _oPrivateColorsSetDefaultPaletteColors
5
+ /// @see oPrivateColorsSetColor
6
+ /// @type List
7
+ /// @access private
8
+ $_o-pf-colors-default-palette-colors: () !default;
9
+
10
+ /// A map to define default tones in the palette.
11
+ /// This is also used to decide which colours are
12
+ /// allowed to have tones, when users request a
13
+ /// non-default tone.
14
+ /// @see _oPrivateColorsSetPaletteTones
15
+ /// @type Map
16
+ /// @access private
17
+ $_o-pf-colors-default-palette-tones: ();
18
+
19
+ /// A map to define default mixes in the palette.
20
+ /// @see _oPrivateColorsSetPaletteTones
21
+ /// @type Map
22
+ /// @access private
23
+ $_o-pf-colors-default-palette-mixes: ();
24
+
25
+ /// A map to define default colour usecases:
26
+ /// A list of arguments for `oPrivateColorsSetUseCase`, except the project name.
27
+ ///
28
+ /// @see _oPrivateColorsSetDefaultPaletteColors
29
+ /// @see oPrivateColorsSetUseCase
30
+ /// @type List
31
+ /// @access private
32
+ $_o-pf-colors-default-usecases: () !default;
33
+
34
+ /// A map to store all set colour usecases.
35
+ /// @see oPrivateColorsSetUseCase
36
+ /// @type Map
37
+ /// @access private
38
+ $_o-pf-colors-usecases: () !default;
39
+
40
+ /// A map to store all set palette colours.
41
+ /// @see oPrivateColorsSetColor
42
+ /// @type Map
43
+ /// @access private
44
+ $_o-pf-colors-palette: () !default;
45
+
46
+ /// Function errors may return a string rather than error
47
+ /// when in test mode, so automated tests may assert errors.
48
+ /// @access private
49
+ /// @type Bool
50
+ $_o-pf-colors-test-environment: false !default;
@@ -0,0 +1,6 @@
1
+ @import '@financial-times/math/index';
2
+ @import '@financial-times/o-brand/main';
3
+
4
+ @import 'variables';
5
+ @import 'palette';
6
+ @import 'functions';