mostly filebased Content Presentation System

_include-media.scss 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. @charset "UTF-8";
  2. // _ _ _ _ _
  3. // (_) | | | | | (_)
  4. // _ _ __ ___| |_ _ __| | ___ _ __ ___ ___ __| |_ __ _
  5. // | | '_ \ / __| | | | |/ _` |/ _ \ | '_ ` _ \ / _ \/ _` | |/ _` |
  6. // | | | | | (__| | |_| | (_| | __/ | | | | | | __/ (_| | | (_| |
  7. // |_|_| |_|\___|_|\__,_|\__,_|\___| |_| |_| |_|\___|\__,_|_|\__,_|
  8. //
  9. // Simple, elegant and maintainable media queries in Sass
  10. // v1.4.9
  11. //
  12. // https://eduardoboucas.github.io/include-media
  13. //
  14. // Authors: Eduardo Boucas (@eduardoboucas)
  15. // Hugo Giraudel (@hugogiraudel)
  16. //
  17. // This project is licensed under the terms of the MIT license
  18. ////
  19. /// include-media library public configuration
  20. /// @author Eduardo Boucas
  21. /// @access public
  22. ////
  23. ///
  24. /// Creates a list of global breakpoints
  25. ///
  26. /// @example scss - Creates a single breakpoint with the label `phone`
  27. /// $breakpoints: ('phone': 320px);
  28. ///
  29. $breakpoints: (
  30. 'phone': 320px,
  31. 'tablet': 768px,
  32. 'desktop': 1024px
  33. ) !default;
  34. ///
  35. /// Creates a list of static expressions or media types
  36. ///
  37. /// @example scss - Creates a single media type (screen)
  38. /// $media-expressions: ('screen': 'screen');
  39. ///
  40. /// @example scss - Creates a static expression with logical disjunction (OR operator)
  41. /// $media-expressions: (
  42. /// 'retina2x': '(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)'
  43. /// );
  44. ///
  45. $media-expressions: (
  46. 'screen': 'screen',
  47. 'print': 'print',
  48. 'handheld': 'handheld',
  49. 'landscape': '(orientation: landscape)',
  50. 'portrait': '(orientation: portrait)',
  51. 'retina2x': '(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi), (min-resolution: 2dppx)',
  52. 'retina3x': '(-webkit-min-device-pixel-ratio: 3), (min-resolution: 350dpi), (min-resolution: 3dppx)'
  53. ) !default;
  54. ///
  55. /// Defines a number to be added or subtracted from each unit when declaring breakpoints with exclusive intervals
  56. ///
  57. /// @example scss - Interval for pixels is defined as `1` by default
  58. /// @include media('>128px') {}
  59. ///
  60. /// /* Generates: */
  61. /// @media (min-width: 129px) {}
  62. ///
  63. /// @example scss - Interval for ems is defined as `0.01` by default
  64. /// @include media('>20em') {}
  65. ///
  66. /// /* Generates: */
  67. /// @media (min-width: 20.01em) {}
  68. ///
  69. /// @example scss - Interval for rems is defined as `0.1` by default, to be used with `font-size: 62.5%;`
  70. /// @include media('>2.0rem') {}
  71. ///
  72. /// /* Generates: */
  73. /// @media (min-width: 2.1rem) {}
  74. ///
  75. $unit-intervals: (
  76. 'px': 1,
  77. 'em': 0.01,
  78. 'rem': 0.1,
  79. '': 0
  80. ) !default;
  81. ///
  82. /// Defines whether support for media queries is available, useful for creating separate stylesheets
  83. /// for browsers that don't support media queries.
  84. ///
  85. /// @example scss - Disables support for media queries
  86. /// $im-media-support: false;
  87. /// @include media('>=tablet') {
  88. /// .foo {
  89. /// color: tomato;
  90. /// }
  91. /// }
  92. ///
  93. /// /* Generates: */
  94. /// .foo {
  95. /// color: tomato;
  96. /// }
  97. ///
  98. $im-media-support: true !default;
  99. ///
  100. /// Selects which breakpoint to emulate when support for media queries is disabled. Media queries that start at or
  101. /// intercept the breakpoint will be displayed, any others will be ignored.
  102. ///
  103. /// @example scss - This media query will show because it intercepts the static breakpoint
  104. /// $im-media-support: false;
  105. /// $im-no-media-breakpoint: 'desktop';
  106. /// @include media('>=tablet') {
  107. /// .foo {
  108. /// color: tomato;
  109. /// }
  110. /// }
  111. ///
  112. /// /* Generates: */
  113. /// .foo {
  114. /// color: tomato;
  115. /// }
  116. ///
  117. /// @example scss - This media query will NOT show because it does not intercept the desktop breakpoint
  118. /// $im-media-support: false;
  119. /// $im-no-media-breakpoint: 'tablet';
  120. /// @include media('>=desktop') {
  121. /// .foo {
  122. /// color: tomato;
  123. /// }
  124. /// }
  125. ///
  126. /// /* No output */
  127. ///
  128. $im-no-media-breakpoint: 'desktop' !default;
  129. ///
  130. /// Selects which media expressions are allowed in an expression for it to be used when media queries
  131. /// are not supported.
  132. ///
  133. /// @example scss - This media query will show because it intercepts the static breakpoint and contains only accepted media expressions
  134. /// $im-media-support: false;
  135. /// $im-no-media-breakpoint: 'desktop';
  136. /// $im-no-media-expressions: ('screen');
  137. /// @include media('>=tablet', 'screen') {
  138. /// .foo {
  139. /// color: tomato;
  140. /// }
  141. /// }
  142. ///
  143. /// /* Generates: */
  144. /// .foo {
  145. /// color: tomato;
  146. /// }
  147. ///
  148. /// @example scss - This media query will NOT show because it intercepts the static breakpoint but contains a media expression that is not accepted
  149. /// $im-media-support: false;
  150. /// $im-no-media-breakpoint: 'desktop';
  151. /// $im-no-media-expressions: ('screen');
  152. /// @include media('>=tablet', 'retina2x') {
  153. /// .foo {
  154. /// color: tomato;
  155. /// }
  156. /// }
  157. ///
  158. /// /* No output */
  159. ///
  160. $im-no-media-expressions: ('screen', 'portrait', 'landscape') !default;
  161. ////
  162. /// Cross-engine logging engine
  163. /// @author Hugo Giraudel
  164. /// @access private
  165. ////
  166. ///
  167. /// Log a message either with `@error` if supported
  168. /// else with `@warn`, using `feature-exists('at-error')`
  169. /// to detect support.
  170. ///
  171. /// @param {String} $message - Message to log
  172. ///
  173. @function im-log($message) {
  174. @if feature-exists('at-error') {
  175. @error $message;
  176. } @else {
  177. @warn $message;
  178. $_: noop();
  179. }
  180. @return $message;
  181. }
  182. ///
  183. /// Wrapper mixin for the log function so it can be used with a more friendly
  184. /// API than `@if im-log('..') {}` or `$_: im-log('..')`. Basically, use the function
  185. /// within functions because it is not possible to include a mixin in a function
  186. /// and use the mixin everywhere else because it's much more elegant.
  187. ///
  188. /// @param {String} $message - Message to log
  189. ///
  190. @mixin log($message) {
  191. @if im-log($message) {}
  192. }
  193. ///
  194. /// Function with no `@return` called next to `@warn` in Sass 3.3
  195. /// to trigger a compiling error and stop the process.
  196. ///
  197. @function noop() {}
  198. ///
  199. /// Determines whether a list of conditions is intercepted by the static breakpoint.
  200. ///
  201. /// @param {Arglist} $conditions - Media query conditions
  202. ///
  203. /// @return {Boolean} - Returns true if the conditions are intercepted by the static breakpoint
  204. ///
  205. @function im-intercepts-static-breakpoint($conditions...) {
  206. $no-media-breakpoint-value: map-get($breakpoints, $im-no-media-breakpoint);
  207. @if not $no-media-breakpoint-value {
  208. @if im-log('`#{$im-no-media-breakpoint}` is not a valid breakpoint.') {}
  209. }
  210. @each $condition in $conditions {
  211. @if not map-has-key($media-expressions, $condition) {
  212. $operator: get-expression-operator($condition);
  213. $prefix: get-expression-prefix($operator);
  214. $value: get-expression-value($condition, $operator);
  215. @if ($prefix == 'max' and $value <= $no-media-breakpoint-value) or
  216. ($prefix == 'min' and $value > $no-media-breakpoint-value) {
  217. @return false;
  218. }
  219. } @else if not index($im-no-media-expressions, $condition) {
  220. @return false;
  221. }
  222. }
  223. @return true;
  224. }
  225. ////
  226. /// Parsing engine
  227. /// @author Hugo Giraudel
  228. /// @access private
  229. ////
  230. ///
  231. /// Get operator of an expression
  232. ///
  233. /// @param {String} $expression - Expression to extract operator from
  234. ///
  235. /// @return {String} - Any of `>=`, `>`, `<=`, `<`, `≥`, `≤`
  236. ///
  237. @function get-expression-operator($expression) {
  238. @each $operator in ('>=', '>', '<=', '<', '≥', '≤') {
  239. @if str-index($expression, $operator) {
  240. @return $operator;
  241. }
  242. }
  243. // It is not possible to include a mixin inside a function, so we have to
  244. // rely on the `im-log(..)` function rather than the `log(..)` mixin. Because
  245. // functions cannot be called anywhere in Sass, we need to hack the call in
  246. // a dummy variable, such as `$_`. If anybody ever raise a scoping issue with
  247. // Sass 3.3, change this line in `@if im-log(..) {}` instead.
  248. $_: im-log('No operator found in `#{$expression}`.');
  249. }
  250. ///
  251. /// Get dimension of an expression, based on a found operator
  252. ///
  253. /// @param {String} $expression - Expression to extract dimension from
  254. /// @param {String} $operator - Operator from `$expression`
  255. ///
  256. /// @return {String} - `width` or `height` (or potentially anything else)
  257. ///
  258. @function get-expression-dimension($expression, $operator) {
  259. $operator-index: str-index($expression, $operator);
  260. $parsed-dimension: str-slice($expression, 0, $operator-index - 1);
  261. $dimension: 'width';
  262. @if str-length($parsed-dimension) > 0 {
  263. $dimension: $parsed-dimension;
  264. }
  265. @return $dimension;
  266. }
  267. ///
  268. /// Get dimension prefix based on an operator
  269. ///
  270. /// @param {String} $operator - Operator
  271. ///
  272. /// @return {String} - `min` or `max`
  273. ///
  274. @function get-expression-prefix($operator) {
  275. @return if(index(('<', '<=', '≤'), $operator), 'max', 'min');
  276. }
  277. ///
  278. /// Get value of an expression, based on a found operator
  279. ///
  280. /// @param {String} $expression - Expression to extract value from
  281. /// @param {String} $operator - Operator from `$expression`
  282. ///
  283. /// @return {Number} - A numeric value
  284. ///
  285. @function get-expression-value($expression, $operator) {
  286. $operator-index: str-index($expression, $operator);
  287. $value: str-slice($expression, $operator-index + str-length($operator));
  288. @if map-has-key($breakpoints, $value) {
  289. $value: map-get($breakpoints, $value);
  290. } @else {
  291. $value: to-number($value);
  292. }
  293. $interval: map-get($unit-intervals, unit($value));
  294. @if not $interval {
  295. // It is not possible to include a mixin inside a function, so we have to
  296. // rely on the `im-log(..)` function rather than the `log(..)` mixin. Because
  297. // functions cannot be called anywhere in Sass, we need to hack the call in
  298. // a dummy variable, such as `$_`. If anybody ever raise a scoping issue with
  299. // Sass 3.3, change this line in `@if im-log(..) {}` instead.
  300. $_: im-log('Unknown unit `#{unit($value)}`.');
  301. }
  302. @if $operator == '>' {
  303. $value: $value + $interval;
  304. } @else if $operator == '<' {
  305. $value: $value - $interval;
  306. }
  307. @return $value;
  308. }
  309. ///
  310. /// Parse an expression to return a valid media-query expression
  311. ///
  312. /// @param {String} $expression - Expression to parse
  313. ///
  314. /// @return {String} - Valid media query
  315. ///
  316. @function parse-expression($expression) {
  317. // If it is part of $media-expressions, it has no operator
  318. // then there is no need to go any further, just return the value
  319. @if map-has-key($media-expressions, $expression) {
  320. @return map-get($media-expressions, $expression);
  321. }
  322. $operator: get-expression-operator($expression);
  323. $dimension: get-expression-dimension($expression, $operator);
  324. $prefix: get-expression-prefix($operator);
  325. $value: get-expression-value($expression, $operator);
  326. @return '(#{$prefix}-#{$dimension}: #{$value})';
  327. }
  328. ///
  329. /// Slice `$list` between `$start` and `$end` indexes
  330. ///
  331. /// @access private
  332. ///
  333. /// @param {List} $list - List to slice
  334. /// @param {Number} $start [1] - Start index
  335. /// @param {Number} $end [length($list)] - End index
  336. ///
  337. /// @return {List} Sliced list
  338. ///
  339. @function slice($list, $start: 1, $end: length($list)) {
  340. @if length($list) < 1 or $start > $end {
  341. @return ();
  342. }
  343. $result: ();
  344. @for $i from $start through $end {
  345. $result: append($result, nth($list, $i));
  346. }
  347. @return $result;
  348. }
  349. ////
  350. /// String to number converter
  351. /// @author Hugo Giraudel
  352. /// @access private
  353. ////
  354. ///
  355. /// Casts a string into a number
  356. ///
  357. /// @param {String | Number} $value - Value to be parsed
  358. ///
  359. /// @return {Number}
  360. ///
  361. @function to-number($value) {
  362. @if type-of($value) == 'number' {
  363. @return $value;
  364. } @else if type-of($value) != 'string' {
  365. $_: im-log('Value for `to-number` should be a number or a string.');
  366. }
  367. $first-character: str-slice($value, 1, 1);
  368. $result: 0;
  369. $digits: 0;
  370. $minus: ($first-character == '-');
  371. $numbers: ('0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9);
  372. // Remove +/- sign if present at first character
  373. @if ($first-character == '+' or $first-character == '-') {
  374. $value: str-slice($value, 2);
  375. }
  376. @for $i from 1 through str-length($value) {
  377. $character: str-slice($value, $i, $i);
  378. @if not (index(map-keys($numbers), $character) or $character == '.') {
  379. @return to-length(if($minus, -$result, $result), str-slice($value, $i))
  380. }
  381. @if $character == '.' {
  382. $digits: 1;
  383. } @else if $digits == 0 {
  384. $result: $result * 10 + map-get($numbers, $character);
  385. } @else {
  386. $digits: $digits * 10;
  387. $result: $result + map-get($numbers, $character) / $digits;
  388. }
  389. }
  390. @return if($minus, -$result, $result);
  391. }
  392. ///
  393. /// Add `$unit` to `$value`
  394. ///
  395. /// @param {Number} $value - Value to add unit to
  396. /// @param {String} $unit - String representation of the unit
  397. ///
  398. /// @return {Number} - `$value` expressed in `$unit`
  399. ///
  400. @function to-length($value, $unit) {
  401. $units: ('px': 1px, 'cm': 1cm, 'mm': 1mm, '%': 1%, 'ch': 1ch, 'pc': 1pc, 'in': 1in, 'em': 1em, 'rem': 1rem, 'pt': 1pt, 'ex': 1ex, 'vw': 1vw, 'vh': 1vh, 'vmin': 1vmin, 'vmax': 1vmax);
  402. @if not index(map-keys($units), $unit) {
  403. $_: im-log('Invalid unit `#{$unit}`.');
  404. }
  405. @return $value * map-get($units, $unit);
  406. }
  407. ///
  408. /// This mixin aims at redefining the configuration just for the scope of
  409. /// the call. It is helpful when having a component needing an extended
  410. /// configuration such as custom breakpoints (referred to as tweakpoints)
  411. /// for instance.
  412. ///
  413. /// @author Hugo Giraudel
  414. ///
  415. /// @param {Map} $tweakpoints [()] - Map of tweakpoints to be merged with `$breakpoints`
  416. /// @param {Map} $tweak-media-expressions [()] - Map of tweaked media expressions to be merged with `$media-expression`
  417. ///
  418. /// @example scss - Extend the global breakpoints with a tweakpoint
  419. /// @include media-context(('custom': 678px)) {
  420. /// .foo {
  421. /// @include media('>phone', '<=custom') {
  422. /// // ...
  423. /// }
  424. /// }
  425. /// }
  426. ///
  427. /// @example scss - Extend the global media expressions with a custom one
  428. /// @include media-context($tweak-media-expressions: ('all': 'all')) {
  429. /// .foo {
  430. /// @include media('all', '>phone') {
  431. /// // ...
  432. /// }
  433. /// }
  434. /// }
  435. ///
  436. /// @example scss - Extend both configuration maps
  437. /// @include media-context(('custom': 678px), ('all': 'all')) {
  438. /// .foo {
  439. /// @include media('all', '>phone', '<=custom') {
  440. /// // ...
  441. /// }
  442. /// }
  443. /// }
  444. ///
  445. @mixin media-context($tweakpoints: (), $tweak-media-expressions: ()) {
  446. // Save global configuration
  447. $global-breakpoints: $breakpoints;
  448. $global-media-expressions: $media-expressions;
  449. // Update global configuration
  450. $breakpoints: map-merge($breakpoints, $tweakpoints) !global;
  451. $media-expressions: map-merge($media-expressions, $tweak-media-expressions) !global;
  452. @content;
  453. // Restore global configuration
  454. $breakpoints: $global-breakpoints !global;
  455. $media-expressions: $global-media-expressions !global;
  456. }
  457. ////
  458. /// include-media public exposed API
  459. /// @author Eduardo Boucas
  460. /// @access public
  461. ////
  462. ///
  463. /// Generates a media query based on a list of conditions
  464. ///
  465. /// @param {Arglist} $conditions - Media query conditions
  466. ///
  467. /// @example scss - With a single set breakpoint
  468. /// @include media('>phone') { }
  469. ///
  470. /// @example scss - With two set breakpoints
  471. /// @include media('>phone', '<=tablet') { }
  472. ///
  473. /// @example scss - With custom values
  474. /// @include media('>=358px', '<850px') { }
  475. ///
  476. /// @example scss - With set breakpoints with custom values
  477. /// @include media('>desktop', '<=1350px') { }
  478. ///
  479. /// @example scss - With a static expression
  480. /// @include media('retina2x') { }
  481. ///
  482. /// @example scss - Mixing everything
  483. /// @include media('>=350px', '<tablet', 'retina3x') { }
  484. ///
  485. @mixin media($conditions...) {
  486. @if ($im-media-support and length($conditions) == 0) or
  487. (not $im-media-support and im-intercepts-static-breakpoint($conditions...)) {
  488. @content;
  489. } @else if ($im-media-support and length($conditions) > 0) {
  490. @media #{unquote(parse-expression(nth($conditions, 1)))} {
  491. // Recursive call
  492. @include media(slice($conditions, 2)...) {
  493. @content;
  494. }
  495. }
  496. }
  497. }