Skip to content

Instantly share code, notes, and snippets.

@FlyingDR
Created January 4, 2019 18:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FlyingDR/81ce09b9226f8ad0182d08ea764fe02a to your computer and use it in GitHub Desktop.
Save FlyingDR/81ce09b9226f8ad0182d08ea764fe02a to your computer and use it in GitHub Desktop.
Customizable CSS grid layouts with support for IE 11
// Excerpt of configuration to display layout definitions
@include r-config(desktop, (
media-query: desktop,
content: (
layout: (
template: (
regular2: (
columns: 1fr 1fr,
rows: auto,
),
regular4: (
columns: 1fr 1fr,
rows: auto auto,
),
regular6: (
columns: 1fr 1fr,
rows: auto auto auto,
),
regular8: (
columns: 1fr 1fr,
rows: auto auto auto auto,
),
large-1small: (
columns: 2fr 1fr,
rows: auto,
item-style: (
(
type: opinion,
style: small,
scope: not-first,
),
),
),
large-2small: (
columns: 2fr 1fr 1fr,
rows: auto,
item-style: (
(
type: opinion,
style: small,
scope: not-first,
),
),
),
large-4small: (
columns: 2fr 1fr 1fr,
rows: auto auto,
cells: (
1: 1 1 1 2,
),
item-style: (
(
scope: first,
style: large,
),
(
scope: not-first,
style: small,
),
),
),
large-4related: (
columns: 2fr 1fr,
rows: auto auto auto auto,
cells: (
1: 1 1 1 4,
),
column-gap: 3,
row-gap: 2,
item-style: (
(
scope: first,
style: large,
),
(
scope: not-first,
type: related,
),
),
),
three: (
columns: 1fr 1fr 1fr,
rows: auto,
),
four: (
columns: 1fr 1fr 1fr 1fr,
rows: auto,
item-style: (
(
type: opinion,
style: small,
scope: true,
),
),
),
five: (
columns: 1fr 1fr 1fr,
rows: auto auto,
cells: (
1: 1 1 2,
),
item-style: (
(
type: opinion,
styl: small,
scope: not-first,
),
),
),
related: (
columns: 1fr 1fr 1fr,
rows: auto auto,
column-gap: 5,
item-style: (
(
type: related,
style: only-title,
scope: true,
),
),
),
article-aside: (
columns: 1fr,
rows: auto auto auto auto auto,
item-style: (
(
style: aside,
scope: true,
),
(
style: aside-small,
scope: not-first,
),
),
),
opinion: (
columns: 1fr 1fr 1fr,
rows: auto,
item-style: (
(
style: large,
scope: '.with-primary-opinion:first-child',
),
),
),
),
column-gap: 2,
row-gap: 2,
),
),
));
@function ie-grid-definition($grid, $gap) {
$result: ();
$size: length($grid);
@for $i from 1 through $size {
$result: append($result, nth($grid, $i));
@if ($i < $size) {
$result: append($result, $gap);
}
}
@return $result;
}
@function ie-grid-cell-index($index) {
@return ($index - 1) * 2 + 1;
}
@function grid-parse-cell-definition($cell) {
@if (length($cell) < 2) {
@return null;
}
$column: nth($cell, 1);
$row: nth($cell, 2);
$column-span: null;
$row-span: null;
@if (length($cell) >= 3 and nth($cell, 3) != null) {
$column-span: nth($cell, 3);
}
@if (length($cell) >= 4 and nth($cell, 4) != null) {
$row-span: nth($cell, 4);
}
@return (
column: $column,
column-span: $column-span,
row: $row,
row-span: $row-span,
);
}
%content-layout {
display: grid;
@include r-each-if('/content/layout/column-gap') {
grid-column-gap: v(r-get());
}
@include r-each-if('/content/layout/row-gap') {
grid-row-gap: v(r-get());
}
@include ie11() {
display: -ms-grid;
-ms-grid-column-align: stretch;
-ms-grid-row-align: stretch;
}
}
@import "layout/related";
@import "layout/article-aside";
@include r-with('/content/layout') {
$content-layout-template-names: map-keys(r-get('template', $id: $responsive-default-id));
@each $template in $content-layout-template-names {
$prefix: 'template/#{$template}';
.cbl-#{$template} {
@extend %content-layout;
@include r-each-if('#{$prefix}/columns') {
@if (not r-has('#{$prefix}/rows')) {
@error 'Columns and rows for grid template "#{$template}" should be defined simultaneously, check "#{r-id()}" responsive configuration';
}
$columns: r-get();
$rows: r-get('#{$prefix}/rows');
grid-template-columns: $columns;
grid-template-rows: $rows;
@include ie11() {
$column-gap: v(r-get-effective('column-gap'));
$row-gap: v(r-get-effective('row-gap'));
-ms-grid-columns: ie-grid-definition($columns, $column-gap);
-ms-grid-rows: ie-grid-definition($rows, $row-gap);
}
}
@include r-each-if('#{$prefix}/column-gap') {
grid-column-gap: v(r-get());
}
@include r-each-if('#{$prefix}/row-gap') {
grid-row-gap: v(r-get());
}
.content-item {
@include r-each-if('#{$prefix}/cells') {
$cells: r-get();
@each $index, $cell in $cells {
@if ($cell != null) {
&:nth-child(#{$index}) {
$definition: grid-parse-cell-definition($cell);
@if ($definition == null) {
@error 'Unsupported grid cell definition format "#{$cell}" is given for cell #{$index} in grid template "#{$template}" in "#{r-id()}" responsive configuration';
}
$column: map-get($definition, column);
$row: map-get($definition, row);
$column-span: map-get($definition, column-span);
$row-span: map-get($definition, row-span);
@if ($column-span != null and $column-span > 1) {
grid-column: $column / span $column-span;
} @else {
grid-column: $column;
}
@if ($row-span != null and $row-span > 1) {
grid-row: $row / span $row-span;
} @else {
grid-row: $row;
}
}
}
}
}
// Handle enforcement of content item styles within layouts
@include r-each-if('#{$prefix}/item-style') {
$modifications: r-get();
@each $params in $modifications {
$params: map-merge((
type: true,
style: null,
scope: false,
), $params);
$type: map-get($params, type);
$style: map-get($params, style);
$scope: map-get($params, scope);
@if ($type and $style and $scope) {
$class: '';
@if ($type != true and $type != 'content') {
$class: '.with-#{$type}';
}
&#{$class} {
@if ($scope == first) {
$scope: ':first-child';
}@if ($scope == last) {
$scope: ':last-child';
} @if ($scope == not-first) {
$scope: ':not(:first-child)';
} @else if ($scope == true) {
$scope: '';
}
&#{$scope} {
@if ($type == true) {
$type: null;
}
@include apply-content-item-template($type, $style);
}
}
}
}
}
// IE 11 requires explicit binding of every child to particular grid cell
// because we use extra cell to define gaps between cells themselves
@include ie11() {
// It is only allowed to display explicitly mapped cells into IE 11
// because of mapping issues, therefore we hide all content items
// and recover display for mapped ones
display: none;
@include r-each-if('/layout', $force-mq: true) {
$columns: r-get-effective('#{$prefix}/columns');
$rows: r-get-effective('#{$prefix}/rows');
$rows-count: length($rows);
$cells: r-get-effective('#{$prefix}/cells');
$columns-count: length($columns);
// Create cells map to determine target cell position of every grid cell
$cells-map: ();
@for $r from 1 through $rows-count {
$rc: ();
@for $c from 1 through $columns-count {
$rc: map-merge($rc, ($c: null));
}
$cells-map: map-merge($cells-map, ($r: $rc));
}
// Map all explicit cells definition to the map
$mapped: ();
@each $index, $cell in $cells {
@if ($cell != null) {
&:nth-child(#{$index}) {
$definition: grid-parse-cell-definition($cell);
$column: map-get($definition, column);
$row: map-get($definition, row);
display: block;
-ms-grid-column: ie-grid-cell-index($column);
-ms-grid-row: ie-grid-cell-index($row);
$column-span: map-get($definition, column-span);
@if ($column-span != null) {
-ms-grid-column-span: ie-grid-cell-index($column-span);
} @else {
$column-span: 1;
}
$row-span: map-get($definition, row-span);
@if ($row-span != null) {
-ms-grid-row-span: ie-grid-cell-index($row-span);
} @else {
$row-span: 1;
}
@for $r from $row through ($row + $row-span - 1) {
@if (not map-has-key($cells-map, $r)) {
@error 'Grid cell row definition is out of bound for cell #{$index} in grid template "#{$template}" in "#{r-id()}" responsive configuration';
}
$rc: map-get($cells-map, $r);
@for $c from $column through ($column + $column-span - 1) {
@if (not map-has-key($rc, $c)) {
@error 'Grid cell column definition is out of bound for cell #{$index} in grid template "#{$template}" in "#{r-id()}" responsive configuration';
}
$rc: map-merge($rc, ($c: $index));
}
$cells-map: map-merge($cells-map, ($r: $rc));
}
$mapped: append($mapped, $index);
}
}
}
// Map unmapped cells into remaining places into grid
$unmapped: ();
@for $i from 1 through ($rows-count * $columns-count) {
@if (index($mapped, $i) == null) {
$unmapped: append($unmapped, $i);
}
}
$index: 1;
@for $r from 1 through $rows-count {
$rc: map-get($cells-map, $r);
@for $c from 1 through $columns-count {
$m: map-get($rc, $c);
@if ($m == null) {
@if ($index <= length($unmapped)) {
$cell-index: nth($unmapped, $index);
&:nth-child(#{$cell-index}) {
display: block;
-ms-grid-column: ie-grid-cell-index($c);
-ms-grid-row: ie-grid-cell-index($r);
}
$index: $index + 1;
}
}
$rc: map-merge($rc, ($c: null));
}
$cells-map: map-merge($cells-map, ($r: $rc));
}
}
}
}
// In a case if content layout is applied to a content block
// with separate background for each content item -
// allow certain content items to escape from defined grid offset
.content-block.cbg-item-color & {
@include r-offset('#{$prefix}/grid-offset', ph);
.content-item {
@include r-each-if('#{$prefix}/escape-offset', $force-mq: true) {
$cells: r-get();
$offset: r-get-effective('#{$prefix}/grid-offset');
@each $index in r-get() {
&:nth-child(#{$index}) {
@include offset($offset, nh);
}
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment