@ulu/frontend 0.4.1 → 0.4.3

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.
@@ -0,0 +1,135 @@
1
+ ////
2
+ /// @group themes
3
+ /// Core configuration module for the themes system.
4
+ /// Used to orchestrate custom property variations (e.g., light/dark modes).
5
+ /// - Note: This system is intended for switching context values like colors, box-shadows, and borders.
6
+ /// It is not recommended to use this for structural layout values like margins or padding.
7
+ /// For responsive or structural changes, rely on the layout/breakpoint modules or component modifiers.
8
+ ////
9
+
10
+ @use "sass:map";
11
+ @use "sass:list";
12
+ @use "sass:meta";
13
+ @use "utils";
14
+ @use "cssvar";
15
+
16
+ /// Module Settings
17
+ /// @type Map
18
+ /// @prop {String} default ["light"] The theme key output to :root
19
+ /// @prop {Map} color-schemes [()] Maps theme names to their corresponding valid CSS color-scheme value (e.g. ("high-contrast": "dark")). Note: If a theme name is literally "light" or "dark", it does not need to be mapped here.
20
+ /// @prop {Map} inverses [()] Maps theme names to their opposite theme name (e.g. ("light": "dark", "dark": "light")). Used to output the .theme-inverse utility.
21
+ $config: (
22
+ "default" : "light",
23
+ "color-schemes" : (),
24
+ "inverses" : ()
25
+ ) !default;
26
+
27
+ /// The themes map
28
+ /// - Format: `("property-name": ("light": value, "dark": value))`
29
+ /// @type Map
30
+
31
+ $tokens: () !default;
32
+
33
+ /// Change modules $config
34
+ /// @param {Map} $changes Map of changes
35
+ /// @example scss
36
+ /// @include ulu.themes-set(( "default" : "dark" ));
37
+
38
+ @mixin set($changes) {
39
+ $config: map.merge($config, $changes) !global;
40
+ }
41
+
42
+ /// Set the theme variations
43
+ /// @param {Map} $changes Map of theme variations to merge
44
+ /// @param {String} $merge-mode Merge mode see utils.map-merge() [null|"deep"|"overwrite"]
45
+ /// @example scss
46
+ /// @include ulu.themes-set-tokens((
47
+ /// "color-background": (
48
+ /// "light": white,
49
+ /// "dark": black
50
+ /// )
51
+ /// ));
52
+
53
+ @mixin set-tokens($changes, $merge-mode: "deep") {
54
+ $tokens: utils.map-merge($tokens, $changes, $merge-mode) !global;
55
+ }
56
+
57
+ /// Get a config option
58
+ /// @param {String} $name Name of property
59
+ /// @example scss {compile} Example usage
60
+ /// .test {
61
+ /// content: ulu.themes-get("default");
62
+ /// }
63
+ /// @return {*} Resolved value
64
+
65
+ @function get($name) {
66
+ @return utils.require-map-get($config, $name, "themes [config]");
67
+ }
68
+
69
+ /// Helper function to safely get the color scheme
70
+ /// @param {String} $theme-name The name of the theme to lookup
71
+ /// @example scss {compile} Example usage
72
+ /// .test {
73
+ /// content: ulu.themes-get-color-scheme("dark");
74
+ /// }
75
+ /// @return {String|Null} The CSS color-scheme value or null
76
+
77
+ @function get-color-scheme($theme-name) {
78
+ $schemes: get("color-schemes");
79
+ $mapped: map.get($schemes, $theme-name);
80
+
81
+ @if ($mapped) {
82
+ @return $mapped;
83
+ }
84
+
85
+ @if ($theme-name == "light" or $theme-name == "dark") {
86
+ @return $theme-name;
87
+ }
88
+
89
+ @warn "ULU Themes: No valid color-scheme mapped for theme '#{$theme-name}'. Please add it using `themes.set(( \"color-schemes\" : ( \"#{$theme-name}\": \"light|dark\" ) ))`. The `color-scheme` property will not be output for this theme.";
90
+ @return null;
91
+ }
92
+
93
+ /// Helper function to get all unique theme keys (e.g., 'light', 'dark') from the $themes map
94
+ /// @return {List} A list of all unique theme string keys
95
+
96
+ @function get-keys() {
97
+ $keys: ();
98
+ @each $prop, $theme-map in $tokens {
99
+ @if meta.type-of($theme-map) == 'map' {
100
+ @each $key, $val in $theme-map {
101
+ @if not list.index($keys, $key) {
102
+ $keys: list.append($keys, $key);
103
+ }
104
+ }
105
+ }
106
+ }
107
+ @return $keys;
108
+ }
109
+
110
+ /// Outputs css vars for a specific type from a theme map
111
+ /// @param {String} $key The key used to retrieve values from the map.
112
+ /// @param {Map} $tokens [$tokens] The map containing the values. Defaults to the internal $tokens map.
113
+ /// @param {String} $prefix The optional prefix for CSS variables.
114
+ /// @example scss {compile} Example usage
115
+ /// @include ulu.themes-set-tokens((
116
+ /// "color-background": (
117
+ /// "light": white,
118
+ /// "dark": black
119
+ /// )
120
+ /// ));
121
+ /// :root {
122
+ /// @include ulu.themes-declare("light");
123
+ /// }
124
+
125
+ @mixin declare($key, $tokens: $tokens, $prefix: cssvar.get("prefix")) {
126
+ @each $name, $definition in $tokens {
127
+ $value: map.get($definition, $key);
128
+ @if ($value) {
129
+ @include cssvar.declare($name, $value, $prefix);
130
+ }
131
+ }
132
+ }
133
+
134
+
135
+
@@ -5,6 +5,7 @@
5
5
 
6
6
  @forward "root" as root-*;
7
7
  @forward "normalize" as normalize-*;
8
+ @forward "themes" as themes-*;
8
9
  @forward "print" as print-*;
9
10
  @forward "color" as color-*;
10
11
  @forward "elements" as elements-*;
@@ -17,6 +18,7 @@
17
18
  @use "../utils";
18
19
  @use "normalize";
19
20
  @use "root";
21
+ @use "themes";
20
22
  @use "elements";
21
23
  @use "print";
22
24
  @use "typography";
@@ -30,6 +32,7 @@
30
32
  $all-includes: (
31
33
  "normalize",
32
34
  "root",
35
+ "themes",
33
36
  "elements,"
34
37
  "print",
35
38
  "elements",
@@ -77,6 +80,9 @@ $current-includes: $all-includes;
77
80
  @if (list.index($includes, "root")) {
78
81
  @include root.styles;
79
82
  }
83
+ @if (list.index($includes, "themes")) {
84
+ @include themes.styles;
85
+ }
80
86
  @if (list.index($includes, "elements")) {
81
87
  @include elements.styles;
82
88
  }
@@ -8,6 +8,53 @@
8
8
  @use "../utils";
9
9
  @use "../cssvar";
10
10
 
11
+ /// Module Settings
12
+ /// @type Map
13
+ /// @prop {Dimension} sticky-top-offset [0px]
14
+ /// @prop {Dimension} sticky-bottom-offset [0px]
15
+
16
+ $config: (
17
+ "sticky-top-offset": 0px,
18
+ "sticky-bottom-offset": 0px
19
+ ) !default;
20
+
21
+ /// Change modules $config
22
+ /// @param {Map} $changes Map of changes
23
+ @mixin set($changes) {
24
+ $config: map.merge($config, $changes) !global;
25
+ }
26
+
27
+ /// Get a config option
28
+ /// @param {String} $name Name of property
29
+ @function get($name) {
30
+ @return utils.require-map-get($config, $name, "base root [config]");
31
+ }
32
+
33
+ /// Global root variables (CSS Custom Properties)
34
+ /// @type Map
35
+ $cssvars: () !default;
36
+
37
+ /// Set global root variables
38
+ /// - Supports mapping values to breakpoints. Values can be literal or a configuration map with `value` and `breakpoints` keys.
39
+ /// @example scss Example usage with breakpoints
40
+ /// @include ulu.base-root-set-cssvars((
41
+ /// "base-color": red,
42
+ /// "responsive-size": (
43
+ /// "value": 1rem,
44
+ /// "breakpoints": (
45
+ /// "medium": (
46
+ /// "direction": "up",
47
+ /// "value": 2rem
48
+ /// )
49
+ /// )
50
+ /// )
51
+ /// ));
52
+ /// @param {Map} $changes Map of changes
53
+ /// @param {String} $merge-mode Merge mode see utils.map-merge() [null|"deep"|"overwrite"]
54
+ @mixin set-cssvars($changes, $merge-mode: null) {
55
+ $cssvars: utils.map-merge($cssvars, $changes, $merge-mode) !global;
56
+ }
57
+
11
58
  /// Output custom properties in :root for base stylesheet
12
59
  /// @example scss
13
60
  /// @include ulu.base-root-styles();
@@ -18,13 +65,14 @@
18
65
  :root {
19
66
  @include custom-properties();
20
67
  @include cssvar.declare-breakpoint();
68
+ @include cssvar.declare-all($cssvars);
21
69
  }
22
70
  }
23
71
 
24
72
  /// Output custom properties for ulu
25
73
 
26
74
  @mixin custom-properties() {
27
- --ulu-sticky-top-offset: 0px; // Should be adjusted by user
28
- --ulu-sticky-bottom-offset: 0px; // Should be adjusted by user
75
+ --ulu-sticky-top-offset: #{ get("sticky-top-offset") }; // Should be adjusted by user
76
+ --ulu-sticky-bottom-offset: #{ get("sticky-bottom-offset") }; // Should be adjusted by user
29
77
  --ulu-scrollbar-width: 0px; // Needs to be set by JS
30
78
  }
@@ -0,0 +1,143 @@
1
+ ////
2
+ /// @group themes
3
+ /// Output base themes stylesheets
4
+ ////
5
+
6
+ @use "sass:list";
7
+ @use "sass:string";
8
+ @use "sass:map";
9
+ @use "../utils";
10
+ @use "../themes";
11
+ @use "../cssvar";
12
+ @use "../selector";
13
+
14
+ /// Module Settings
15
+ /// @type Map
16
+ /// @prop {Boolean} output-inverse [true] Whether to output the `.theme-inverse` utility and corresponding inverse variables
17
+ /// @prop {Color} fake-dark-color [white] The fallback text color applied to elements using the `.theme-dark-fake` utility
18
+ /// @prop {String} fake-dark-filter [invert(1) hue-rotate(180deg) saturate(0.7)] The filter applied to elements using the `.theme-dark-fake` utility
19
+ $config: (
20
+ "output-inverse": true,
21
+ "fake-dark-color": white,
22
+ "fake-dark-filter": invert(1) hue-rotate(180deg) saturate(0.7)
23
+ ) !default;
24
+
25
+ /// Change modules $config
26
+ /// @param {Map} $changes Map of changes
27
+ @mixin set($changes) {
28
+ $config: map.merge($config, $changes) !global;
29
+ }
30
+
31
+ /// Get a config option
32
+ /// @param {String} $name Name of property
33
+ @function get($name) {
34
+ @return utils.require-map-get($config, $name, "base themes [config]");
35
+ }
36
+
37
+ /// Outputs base theme variables and classes
38
+ /// @example scss
39
+ /// @include ulu.base-themes-styles();
40
+ @mixin styles {
41
+ @include utils.file-header('base', 'themes');
42
+
43
+ @if (list.length(themes.$tokens) > 0) {
44
+ $keys: themes.get-keys();
45
+ $default: themes.get("default");
46
+ $prefix: selector.class("theme");
47
+ $dark-selectors: ();
48
+ $inverses: themes.get("inverses");
49
+ $inverse-prefix: selector.class("theme-inverse");
50
+ $output-inverse: get("output-inverse");
51
+
52
+ // 1. Build Base Themes
53
+ @each $key in $keys {
54
+ $selectors: ();
55
+ $selectors: list.append($selectors, "#{ $prefix }-#{ $key }", comma);
56
+
57
+ @if ($key == $default) {
58
+ $selectors: list.append($selectors, ":root", comma);
59
+ }
60
+
61
+ #{ $selectors } {
62
+ // Standard color-scheme and CSS vars
63
+ $color-scheme: themes.get-color-scheme($key);
64
+ @if ($color-scheme) {
65
+ color-scheme: #{ $color-scheme };
66
+ }
67
+ @include themes.declare($key);
68
+
69
+ // Optional Inverse Variables (Variable Swap Approach)
70
+ // Can use @scope in the future but for now we suffix -"inverse"
71
+ @if ($output-inverse) {
72
+ $inverse-key: map.get($inverses, $key);
73
+ @if ($inverse-key) {
74
+ $inv-scheme: themes.get-color-scheme($inverse-key);
75
+ @if ($inv-scheme) {
76
+ --ulu-inverse-color-scheme: #{ $inv-scheme };
77
+ }
78
+
79
+ @each $prop, $theme-map in themes.$tokens {
80
+ @if (utils.is-map($theme-map)) {
81
+ $inv-value: map.get($theme-map, $inverse-key);
82
+ @if ($inv-value) {
83
+ // Only output the inverse variable, not its fallback!
84
+ @include cssvar.declare("inverse-#{$prop}", $inv-value);
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+ // Contextual trigger for the fake dark utility
92
+ @if ($color-scheme == "dark") {
93
+ --ulu-theme-fake-filter: #{ get("fake-dark-filter") };
94
+ --ulu-theme-fake-color: #{ get("fake-dark-color") };
95
+ } @else {
96
+ --ulu-theme-fake-filter: none;
97
+ --ulu-theme-fake-color: inherit;
98
+ }
99
+ }
100
+
101
+ // Collect dark themes for the fake utility
102
+ @if (themes.get-color-scheme($key) == "dark") {
103
+ $dark-selectors: list.append($dark-selectors, $selectors, comma);
104
+ // Ensure fake dark applies to inverted light themes
105
+ @each $current-theme, $inverse-target in $inverses {
106
+ @if ($inverse-target == $key) {
107
+ $dark-selectors: list.append($dark-selectors, "#{ $prefix }-#{ $current-theme } #{ $inverse-prefix }", comma);
108
+ @if ($current-theme == $default) {
109
+ $dark-selectors: list.append($dark-selectors, ":root #{ $inverse-prefix }", comma);
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ // 2. The Inverse Utility Class
117
+ @if ($output-inverse and list.length(map.keys($inverses)) > 0) {
118
+ #{ $inverse-prefix } {
119
+ color-scheme: var(--ulu-inverse-color-scheme, inherit);
120
+ @each $prop, $theme-map in themes.$tokens {
121
+ @if (utils.is-map($theme-map)) {
122
+ $inv-name: cssvar.name("inverse-" + $prop);
123
+ // Swap them: --site-color-bg: var(--site-inverse-color-bg)
124
+ @include cssvar.declare($prop, var(#{ $inv-name }));
125
+ }
126
+ }
127
+ }
128
+ }
129
+
130
+ // 3. The Fake Dark Utility Class
131
+ /// Can be used on elements that need dark mode but that have colors that can't be updated (canvas charts for example)
132
+ /// It inverts the items color, and then inverts/spins the element colors so they match the hue before inversion
133
+ /// It also decreases the contrast slightly
134
+ @if (list.length($dark-selectors) > 0) {
135
+ #{ $dark-selectors } {
136
+ #{ $prefix }-dark-fake {
137
+ filter: var(--ulu-theme-fake-filter, none);
138
+ color: var(--ulu-theme-fake-color, inherit);
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulu/frontend",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "A framework-agnostic frontend toolkit providing a modular, tree-shakable library of accessible components and utilities. Designed for seamless integration, it features a highly configurable SCSS system for any environment and vanilla JavaScript modules optimized for traditional websites and content management systems.",
5
5
  "type": "module",
6
6
  "files": [