Merge "Remove use of BagOStuff TTL constants from unrelated code"
[mediawiki.git] / resources / lib / codex / mixins / css-icon.less
blob5e0604b7c8e6429b4ab3fbfe635a3f1272dc8214
1 @import ( reference ) '@wikimedia/codex-icons/codex-icon-paths.less';
3 //
4 // To create a CSS-only icon you can do one of the following:
5 // 1. Apply the .cdx-mixin-css-icon() mixin to an empty <span>, passing in at least the icon param.
6 //    This method should suffice for any square icon that can exist as a standalone element. This
7 //    mixin applies all of the other mixins inside this file. See Message.vue for sample usage.
8 // 2. Apply the individual CSS icon mixins for background, size, alignment, and/or background-image
9 //    rules to any element. This can be used to apply an icon within another element, like the
10 //    <select> handle. See Select.vue for sample usage.
12 // These mixins account for icons that vary by reading direction or language.
15 // Get the associated min-size-icon from a size-icon token.
16 .get-calculated-min-size-icon( @param-size-icon ) {
17         // Fallback: when an unrecognized value is passed in, don't crash, but use the same value
18         // for size and min-size (T367098). This code runs for all values, but for recognized values
19         // it's overridden by one of the lines below.
20         @calculated-min-size-icon: @param-size-icon;
22 .get-calculated-min-size-icon( @param-size-icon ) when ( @param-size-icon = @size-icon-medium ) {
23         @calculated-min-size-icon: @min-size-icon-medium;
25 .get-calculated-min-size-icon( @param-size-icon ) when ( @param-size-icon = @size-icon-small ) {
26         @calculated-min-size-icon: @min-size-icon-small;
28 .get-calculated-min-size-icon( @param-size-icon ) when ( @param-size-icon = @size-icon-x-small ) {
29         @calculated-min-size-icon: @min-size-icon-x-small;
33 // Get background rules for a CSS icon (or mask rules for icons in buttons).
35 // @param {string} size-icon - The size of the icon, used to set background-size
36 // @param {string} background-position - The background position value
37 // @param {boolean} is-button-icon: Whether this icon is inside of a <button> element.
39 .cdx-mixin-css-icon-background( @param-size-icon: @size-icon-medium, @param-background-position: @background-position-base, @param-is-button-icon: false ) {
40         .get-calculated-min-size-icon( @param-size-icon );
42         // Support Firefox v39-52: fall back to background rules.
43         // Support Chrome: Chrome requires the -webkit prefix so we must use it in all @supports queries.
44         @supports not ( ( -webkit-mask-image: none ) or ( mask-image: none ) ) {
45                 background-position: @param-background-position;
46                 background-repeat: no-repeat;
47                 // Set background size to the relative @param-size-icon or to @calculated-min-size-icon, whichever is larger.
48                 // This ensures that the icon will never appear smaller than @calculated-min-size-icon.
49                 // Escape the max() call to prevent older Less versions from trying to do the max() calculation at compile time.
50                 /* stylelint-disable-next-line plugin/no-unsupported-browser-features */
51                 background-size: calc( ~'max( @{param-size-icon}, @{calculated-min-size-icon} )' );
52         }
54         @supports ( -webkit-mask-image: none ) or ( mask-image: none ) {
55                 /* stylelint-disable plugin/no-unsupported-browser-features */
56                 // Support Chrome
57                 -webkit-mask-size: calc( ~'max( @{param-size-icon}, @{calculated-min-size-icon} )' );
58                 mask-size: calc( ~'max( @{param-size-icon}, @{calculated-min-size-icon} )' );
59                 // Support Chrome
60                 -webkit-mask-repeat: no-repeat;
61                 mask-repeat: no-repeat;
62                 // Support Chrome
63                 -webkit-mask-position: @param-background-position;
64                 mask-position: @param-background-position;
65                 /* stylelint-enable plugin/no-unsupported-browser-features */
66         }
70 // Get size styles for a CSS icon.
72 // This sets min-width, min-height, width, and height for a square icon.
74 // @param {string} size-icon: The size of the icon (base, small, x-small, or indicator)
76 .cdx-mixin-css-icon-size( @param-size-icon: @size-icon-medium ) {
77         .get-calculated-min-size-icon( @param-size-icon );
78         // Set the default icon size.
79         min-width: @calculated-min-size-icon;
80         min-height: @calculated-min-size-icon;
81         // Scale width/height of the span with font size.
82         width: @param-size-icon;
83         height: @param-size-icon;
87 // Get alignment styles for a CSS icon.
89 // @param {string} vertical-align: The vertical-align value
91 .cdx-mixin-css-icon-alignment( @param-vertical-align: text-bottom ) {
92         display: inline-block;
93         // Vertically align surrounding text in inline, inline-block, and table contexts.
94         vertical-align: @param-vertical-align;
97 // Set the color of a mask-image-based icon
99 // @param {hex} fill-color - The fill color of the icon
100 // @param {boolean} is-button-icon - Whether this icon is inside of a <button> element
101 .cdx-mixin-css-icon-mask-image-color( @param-fill-color, @param-is-button-icon ) {
102         // For icons outside of buttons, use background-color to set the icon color.
103         & when not ( @param-is-button-icon ) {
104                 background-color: @param-fill-color;
105         }
106         // For icons within buttons, set transition rules. The icon color will be changed in the
107         // Button component depending on props and state.
108         & when ( @param-is-button-icon ) {
109                 transition-property: @transition-property-icon-css-only;
110                 transition-duration: @transition-duration-base;
111         }
115 // Set the icon image, either via mask-image or background-image.
117 // For browsers that support mask-image, this mixin will apply the background image as a mask, so
118 // that background-color rules in the Button styles can set the icon color.
120 // For browsers that don't support mask-image, this mixin sets the icon as a background image with
121 // the color set via opacity.
123 // @param {string} icon - The icon to show
124 // @param {hex} fill-color - The fill color of the icon (defaults to @color-base)
125 // @param {boolean} is-button-icon - Whether this icon is inside of a <button> element
127 .cdx-mixin-css-icon-try-mask-image( @param-icon, @param-fill-color, @param-is-button-icon ) {
128         @hex-color-black: #000000;
129         @escaped-color-black: escape( @hex-color-black );
130         @image-url: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="@{escaped-color-black}">@{param-icon}</svg>';
132         // Support Firefox v39-52: fall back to background-image.
133         // Support Chrome: Chrome requires the -webkit prefix so we must use it in all @supports queries.
134         @supports not ( ( -webkit-mask-image: none ) or ( mask-image: none ) ) {
135                 background-image: url( @image-url );
136                 // Set icon color to white in night mode.
137                 filter: invert( @filter-invert-icon );
138                 // Set icon color closer to @color-base.
139                 opacity: @opacity-icon-base;
141                 // Fallback for @color-inverted icons.
142                 .cdx-button:not( .cdx-button--weight-quiet ):disabled &,
143                 .cdx-button--weight-primary.cdx-button--action-progressive &,
144                 .cdx-button--weight-primary.cdx-button--action-destructive & {
145                         // Set icon color close to @color-inverted.
146                         filter: invert( @filter-invert-primary-button-icon );
147                 }
148         }
150         // For browsers that support it, use mask-image to set the SVG so we can change the color with
151         // much more granularity.
152         @supports ( -webkit-mask-image: none ) or ( mask-image: none ) {
153                 // Support Chrome
154                 /* stylelint-disable-next-line plugin/no-unsupported-browser-features */
155                 -webkit-mask-image: url( @image-url );
156                 /* stylelint-disable-next-line plugin/no-unsupported-browser-features */
157                 mask-image: url( @image-url );
159                 // Set background-color to color the icon
160                 .cdx-mixin-css-icon-mask-image-color( @param-fill-color, @param-is-button-icon );
161         }
165 // Get rules to set the appropriate image for the icon.
167 // Note that in RTL contexts, this mixin requires `dir="rtl"` either on the icon element itself
168 // or on the <html> element.
170 // This mixin takes in an icon, which is really a Less variable generated by the codex-icons
171 // package. These variables are lists of icon data that contain:
172 // 1. The default icon path (a string)
173 // 2. Whether the icon should flip in RTL ('true' or 'false')
174 // 3. Exceptions to the flip rule ('false' or a selector string that will rule out languages)
175 // 4. RTL-specific icon path ('false' or the path string)
176 // 5. Whether the icon has language-specific variants ('true' or 'false')
177 // 6+ If there are language-specific variants, they will be included as string pairs after the other
178 //   icon data. The first item in the pair is a lang code and the second is the icon path for that
179 //   language.
181 // @param {string} icon - The icon to show (follows the pattern @cdx-icon-icon-name, e.g. @cdx-icon-info-filled )
182 // @param {hex} fill-color - The fill color of the icon (defaults to @color-base)
183 // @param {boolean} is-button-icon - Whether this icon is inside of a <button> element
185 .cdx-mixin-css-icon-background-image( @param-icon, @param-fill-color: @color-base, @param-is-button-icon: false ) {
186         // Extract icon data from the list.
187         @default-icon: extract( @param-icon, 1 );
188         @should-flip: extract( @param-icon, 2 );
189         @flip-exceptions: extract( @param-icon, 3 );
190         @rtl-icon: extract( @param-icon, 4 );
191         @has-lang-variants: extract( @param-icon, 5 );
193         // Add default image.
194         .cdx-mixin-css-icon-try-mask-image( @default-icon, @param-fill-color, @param-is-button-icon );
196         // Flip icons with no shouldFlip exceptions.
197         & when ( @should-flip = 'true' ) and ( @flip-exceptions = 'false' ) {
198                 &[ dir='rtl' ],
199                 html[ dir='rtl' ] &:not( [ dir='ltr' ] ) {
200                         transform: scaleX( -1 );
201                 }
202         }
204         // Flip icons with shouldFlip exceptions.
205         & when ( @should-flip = 'true' ) and not ( @flip-exceptions = 'false' ) {
206                 // Create a selector string out of each exception lang code.
207                 // Final selector will look like `:not( :lang( he ) ):not( :lang( yi ) )`
208                 @exceptions-selector: e( replace( @flip-exceptions, '(^| )([^ ]+)', ':not( :lang( $2 ) )', 'g' ) );
210                 &[ dir='rtl' ],
211                 html[ dir='rtl' ] &:not( [ dir='ltr' ] ) {
212                         &@{exceptions-selector} {
213                                 transform: scaleX( -1 );
214                         }
215                 }
216         }
218         // If an icon has an RTL-specific icon, apply it.
219         & when not ( @rtl-icon = 'false' ) {
220                 &[ dir='rtl' ],
221                 html[ dir='rtl' ] &:not( [ dir='ltr' ] ) {
222                         .cdx-mixin-css-icon-try-mask-image( @rtl-icon, @param-fill-color, @param-is-button-icon );
223                 }
224         }
226         // Set language-specific icons.
227         & when ( @has-lang-variants = 'true' ) {
228                 @icon-list-length: length( @param-icon );
230                 // Language-specific icons are represented by list items in @param-icon. They consist of a
231                 // lang code, e.g. ar, and an icon path.
232                 // Since we can't use modern Less features in MediaWiki, we need a recursive mixin.
233                 .get-lang-rules( @i: 6 ) when ( @i <= @icon-list-length ) {
234                         @lang-data: extract( @param-icon, @i );
235                         @lang-code: extract( @lang-data, 1 );
236                         @lang-icon: extract( @lang-data, 2 );
238                         &:lang( @{lang-code} ) {
239                                 .cdx-mixin-css-icon-try-mask-image( @lang-icon, @param-fill-color, @param-is-button-icon );
240                         }
241                         .get-lang-rules( @i + 1 );
242                 }
244                 .get-lang-rules();
245         }
249 // Create a square, standalone CSS icon.
251 // This mixin only supports icons provided by Codex, which will be embedded as data URLs. To provide
252 // a custom icon (either as a data URL or as a regular URL), set @param-icon to 'none', and set
253 // `mask-image` and `-webkit-mask-image` to the URL of the custom icon.
255 // @param {string} icon - The icon to show (follows the pattern @cdx-icon-icon-name, e.g. @cdx-icon-info-filled), or 'none'
256 // @param {hex} fill-color - The fill color of the icon
257 // @param {string} size-icon: The size of the icon
258 // @param {boolean} is-button-icon: Whether this icon is inside of a <button> element.
259 // @param {string} background-position - The background position value
260 // @param {string} vertical-align: The vertical-align value
262 /* stylelint-disable @stylistic/indentation */
263 .cdx-mixin-css-icon(
264         @param-icon,
265         @param-fill-color: @color-base,
266         @param-size-icon: @size-icon-medium,
267         @param-is-button-icon: false,
268         @param-background-position: @background-position-base,
269         @param-vertical-align: text-bottom
270 ) {
271 /* stylelint-enable @stylistic/indentation */
272         .cdx-mixin-css-icon-background( @param-size-icon, @param-background-position, @param-is-button-icon );
273         .cdx-mixin-css-icon-size( @param-size-icon );
274         .cdx-mixin-css-icon-alignment( @param-vertical-align );
276         & when not ( @param-icon = 'none' ) {
277                 .cdx-mixin-css-icon-background-image( @param-icon, @param-fill-color, @param-is-button-icon );
278         }
280         & when ( @param-icon = 'none' ) {
281                 // The caller is going to set mask-image themselves; but we still need to set the color
282                 .cdx-mixin-css-icon-mask-image-color( @param-fill-color, @param-is-button-icon );
283         }