1 @import ( reference ) '@wikimedia/codex-icons/codex-icon-paths.less';
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} )' );
54 @supports ( -webkit-mask-image: none ) or ( mask-image: none ) {
55 /* stylelint-disable plugin/no-unsupported-browser-features */
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} )' );
60 -webkit-mask-repeat: no-repeat;
61 mask-repeat: no-repeat;
63 -webkit-mask-position: @param-background-position;
64 mask-position: @param-background-position;
65 /* stylelint-enable plugin/no-unsupported-browser-features */
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;
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;
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 );
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 ) {
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 );
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
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' ) {
199 html[ dir='rtl' ] &:not( [ dir='ltr' ] ) {
200 transform: scaleX( -1 );
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' ) );
211 html[ dir='rtl' ] &:not( [ dir='ltr' ] ) {
212 &@{exceptions-selector} {
213 transform: scaleX( -1 );
218 // If an icon has an RTL-specific icon, apply it.
219 & when not ( @rtl-icon = 'false' ) {
221 html[ dir='rtl' ] &:not( [ dir='ltr' ] ) {
222 .cdx-mixin-css-icon-try-mask-image( @rtl-icon, @param-fill-color, @param-is-button-icon );
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 );
241 .get-lang-rules( @i + 1 );
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 */
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
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 );
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 );