From ef164e03d72989303dd5dcc6e197ea17f94f1f76 Mon Sep 17 00:00:00 2001 From: Mason Hu Date: Thu, 28 Mar 2024 10:50:06 +0200 Subject: [PATCH] feat: equal height grid row pattern - Added equal height grid row pattern with full screen sized dividers Signed-off-by: Mason Hu --- releases.yml | 4 + scss/_patterns_equal-height-row.scss | 214 ++++++++++++++++++ scss/_patterns_media-container.scss | 5 + scss/_vanilla.scss | 24 +- .../standalone/patterns_equal-height-row.scss | 11 + side-navigation.yaml | 2 + .../equal-height-row/3-column-row.html | 62 +++++ .../equal-height-row/4-items-per-column.html | 100 ++++++++ .../patterns/equal-height-row/default.html | 73 ++++++ templates/docs/patterns/equal-height-row.md | 90 ++++++++ 10 files changed, 574 insertions(+), 11 deletions(-) create mode 100644 scss/_patterns_equal-height-row.scss create mode 100644 scss/standalone/patterns_equal-height-row.scss create mode 100644 templates/docs/examples/patterns/equal-height-row/3-column-row.html create mode 100644 templates/docs/examples/patterns/equal-height-row/4-items-per-column.html create mode 100644 templates/docs/examples/patterns/equal-height-row/default.html create mode 100644 templates/docs/patterns/equal-height-row.md diff --git a/releases.yml b/releases.yml index 48e9c997e9..e3aee22db1 100644 --- a/releases.yml +++ b/releases.yml @@ -4,6 +4,10 @@ url: /docs/patterns/navigation status: Updated notes: We've updated the navigation to support new theming. + - component: Equal height row + url: /docs/patterns/equal-height-row + status: New + notes: We've introduced new equal height row component (.p-equal-height-row) to better align content across columns. - version: 4.9.0 features: - component: Images diff --git a/scss/_patterns_equal-height-row.scss b/scss/_patterns_equal-height-row.scss new file mode 100644 index 0000000000..86cf518b56 --- /dev/null +++ b/scss/_patterns_equal-height-row.scss @@ -0,0 +1,214 @@ +@import 'settings'; + +// wrapper mixin that provides relevant class selectors for 1st, 2nd and 3rd dividers +@mixin position-divider-by-order-in-grid($divider-order, $large-screen: false) { + // for large screens, dividers are positioned as pseudo elements at row level + @if $large-screen { + @if $divider-order == 1 { + &.has-1st-divider::before { + @content; + } + } + + @if $divider-order == 2 { + &.has-2nd-divider::after { + @content; + } + } + + @if $divider-order == 3 { + // when 3rd-divider is present and 1st-divider is not present + &.has-3rd-divider:not(.has-1st-divider)::before, + // when 3rd-divider is present and 2nd-divider is not present + &.has-3rd-divider:not(.has-2nd-divider)::after { + @content; + } + + // when only 3rd-divider is present + &.has-3rd-divider:not(.has-1st-divider):not(.has-2nd-divider)::before { + @content; + } + + &.has-3rd-divider:not(.has-1st-divider):not(.has-2nd-divider)::after { + display: none; + } + } + } @else { + // for smaller screens, dividers are positioned as pseudo elements at column level + @if $divider-order == 1 { + &.has-1st-divider .p-equal-height-row__col::before { + @content; + } + } + + @if $divider-order == 2 { + &.has-2nd-divider .p-equal-height-row__col::after { + @content; + } + } + + @if $divider-order == 3 { + &.has-3rd-divider:not(.has-1st-divider) .p-equal-height-row__col::before, + &.has-3rd-divider:not(.has-2nd-divider) .p-equal-height-row__col::after { + @content; + } + + &.has-3rd-divider:not(.has-1st-divider):not(.has-2nd-divider) .p-equal-height-row__col::before { + @content; + } + + &.has-3rd-divider:not(.has-1st-divider):not(.has-2nd-divider) .p-equal-height-row__col::after { + display: none; + } + } + } +} + +@mixin divider-styles($large-screen: false) { + // For each row or column grid we only have access to two pseudo elements + // if 1st-divider (::before) is present, assume 2nd-divider (::after) isn't, then 3rd-divider must be (::after) + // if 2nd-divider (::after) is present, assume 1st-divider (::before) isn't, then 3rd-divider must be (::before) + @if $large-screen { + &.has-1st-divider::before, + &.has-2nd-divider::after, + &.has-3rd-divider:not(.has-1st-divider)::before, + &.has-3rd-divider:not(.has-2nd-divider)::after { + @content; + } + } @else { + // For smaller screens, the divider pseudo elements are inserted at the column level + &.has-1st-divider .p-equal-height-row__col::before, + &.has-2nd-divider .p-equal-height-row__col::after, + &.has-3rd-divider:not(.has-1st-divider) .p-equal-height-row__col::before, + &.has-3rd-divider:not(.has-2nd-divider) .p-equal-height-row__col::after { + @content; + } + } +} + +@mixin vf-p-equal-height-row { + .p-equal-height-row { + @extend %vf-row; + padding-bottom: $spv--strip-regular * 0.5; + position: relative; + + .p-equal-height-row__col { + // smaller screens each column will have border top by default + border-top-color: $colors--theme--border-default; + border-top-style: solid; + border-top-width: 1px; + display: grid; + grid-column: span $grid-columns-small; + grid-row: span 4; + grid-template-rows: subgrid; + margin-top: -1px; + padding-bottom: $spv--small; + position: relative; + + @media screen and ($breakpoint-small <= width < $breakpoint-large) { + grid-column: span $grid-columns-medium; + grid-template-columns: subgrid; + + // for medium screen, each column item will take half of the available cols from the parent grid + .p-equal-height-row__item { + grid-column: span calc($grid-columns-medium / 2); + } + + // for medium screen, position the first column item on the left of the grid + .p-equal-height-row__item:first-child { + grid-row: span 100; // this needs to be sufficiently large so remaining column items won't get stretched + } + } + + @media screen and (width >= $breakpoint-large) { + border: none; + grid-column: span calc($grid-columns / 4); + padding-bottom: 0; + } + } + + // divider styles + @include divider-styles($large-screen: false) { + @extend %vf-pseudo-border; + } + + // remove row level dividers for smaller screen sizes + @include divider-styles($large-screen: true) { + @extend %vf-pseudo-border; + display: none; + } + + @include position-divider-by-order-in-grid(1) { + grid-row: 2; + } + + @include position-divider-by-order-in-grid(2) { + grid-row: 3; + } + + @include position-divider-by-order-in-grid(3) { + grid-row: 4; + } + + @media screen and ($breakpoint-small <= width < $breakpoint-large) { + // We don't need to insert divider below item-1 for medium screen size since item-1 gets positioned on the left + @include position-divider-by-order-in-grid(1) { + display: none; + } + + @include position-divider-by-order-in-grid(2) { + grid-column: 4 / 7; + grid-row: 2; + } + + @include position-divider-by-order-in-grid(3) { + grid-column: 4 / 7; + grid-row: 3; + } + } + + @media screen and (width >= $breakpoint-large) { + padding-bottom: $spv--strip-regular; + + // remove column level dividers for large screen sizes + @include divider-styles($large-screen: false) { + display: none; + } + + @include divider-styles($large-screen: true) { + display: block; + grid-column-end: span 12; + grid-column-start: 1; + margin: auto; + } + + @include position-divider-by-order-in-grid(1, $large-screen: true) { + grid-row: 2; + } + + @include position-divider-by-order-in-grid(2, $large-screen: true) { + grid-row: 3; + } + + @include position-divider-by-order-in-grid(3, $large-screen: true) { + grid-row: 4; + } + } + } + + // support for 25-75 split on large screen size for this pattern + .row--25-75-on-large > .col, + .row > .col-9 { + & .p-equal-height-row { + grid-template-columns: repeat($grid-columns-small, minmax(0, 1fr)); + + @media screen and ($breakpoint-small <= width < $breakpoint-large) { + grid-template-columns: repeat($grid-columns-medium, minmax(0, 1fr)); + } + + @media screen and (width >= $breakpoint-large) { + grid-template-columns: repeat(9, minmax(0, 1fr)); + } + } + } +} diff --git a/scss/_patterns_media-container.scss b/scss/_patterns_media-container.scss index 10cb58efb0..bfd37d3e6d 100644 --- a/scss/_patterns_media-container.scss +++ b/scss/_patterns_media-container.scss @@ -1,5 +1,10 @@ @mixin vf-p-media-container { + // stylelint-disable selector-max-type -- media container can use selector type .p-media-container { margin-top: $spv--small; + img { + display: block; + } } + // stylelint-enable selector-max-type } diff --git a/scss/_vanilla.scss b/scss/_vanilla.scss index ae5e5499e1..e290cf0e6b 100644 --- a/scss/_vanilla.scss +++ b/scss/_vanilla.scss @@ -4,18 +4,19 @@ @import 'patterns_accordion'; @import 'patterns_article-pagination'; +@import 'patterns_badge'; @import 'patterns_breadcrumbs'; @import 'patterns_buttons'; @import 'patterns_card'; @import 'patterns_chip'; -@import 'patterns_badge'; @import 'patterns_code-snippet'; @import 'patterns_contextual-menu'; @import 'patterns_divider'; +@import 'patterns_equal-height-row'; @import 'patterns_form-help-text'; -@import 'patterns_form-validation'; -@import 'patterns_form-tick-elements'; @import 'patterns_form-password-toggle'; +@import 'patterns_form-tick-elements'; +@import 'patterns_form-validation'; @import 'patterns_forms'; @import 'patterns_grid'; @import 'patterns_heading-icon'; @@ -39,22 +40,22 @@ @import 'patterns_search-and-filter'; @import 'patterns_search-box'; @import 'patterns_section'; +@import 'patterns_segmented-control'; @import 'patterns_separator'; -@import 'patterns_side-navigation'; @import 'patterns_side-navigation-expandable'; +@import 'patterns_side-navigation'; @import 'patterns_slider'; @import 'patterns_status-label'; @import 'patterns_strip'; +@import 'patterns_suru'; @import 'patterns_switch'; -@import 'patterns_segmented-control'; -@import 'patterns_table-icons'; @import 'patterns_table-expanding'; -@import 'patterns_table-of-contents'; +@import 'patterns_table-icons'; @import 'patterns_table-mobile-card'; +@import 'patterns_table-of-contents'; @import 'patterns_table-sortable'; @import 'patterns_tabs'; @import 'patterns_tooltips'; -@import 'patterns_suru'; // Layouts @import 'layouts_application'; @@ -96,22 +97,23 @@ // Patterns @include vf-p-accordion; @include vf-p-article-pagination; + @include vf-p-badge; @include vf-p-breadcrumbs; @include vf-p-buttons; @include vf-p-card; @include vf-p-chip; - @include vf-p-section; - @include vf-p-badge; @include vf-p-code-snippet; @include vf-p-contextual-menu; @include vf-p-divider; + @include vf-p-equal-height-row; @include vf-p-form-help-text; - @include vf-p-form-validation; @include vf-p-form-tick-elements; + @include vf-p-form-validation; @include vf-p-forms; @include vf-p-grid; @include vf-p-heading-icon; @include vf-p-headings; + @include vf-p-section; @include vf-p-form-password-toggle; @include vf-p-icons; diff --git a/scss/standalone/patterns_equal-height-row.scss b/scss/standalone/patterns_equal-height-row.scss new file mode 100644 index 0000000000..6be926283b --- /dev/null +++ b/scss/standalone/patterns_equal-height-row.scss @@ -0,0 +1,11 @@ +@import '../vanilla'; +@include vf-base; + +// dependencies needed for examples +@include vf-p-buttons; +@include vf-p-media-container; +@include vf-p-headings; + +// vf-p-grid is needed for this pattern to work +@include vf-p-grid; +@include vf-p-equal-height-row; diff --git a/side-navigation.yaml b/side-navigation.yaml index 8a42c7e09c..9a56582130 100644 --- a/side-navigation.yaml +++ b/side-navigation.yaml @@ -30,6 +30,8 @@ subheadings: - title: Accordion url: /docs/patterns/accordion + - title: Equal height row + url: /docs/patterns/equal-height-row - title: Badge url: /docs/patterns/badge - title: Breadcrumbs diff --git a/templates/docs/examples/patterns/equal-height-row/3-column-row.html b/templates/docs/examples/patterns/equal-height-row/3-column-row.html new file mode 100644 index 0000000000..2e109ac11e --- /dev/null +++ b/templates/docs/examples/patterns/equal-height-row/3-column-row.html @@ -0,0 +1,62 @@ +{% extends "_layouts/examples.html" %} +{% block title %}Equal height row / Three column row{% endblock %} + +{% block standalone_css %}patterns_equal-height-row{% endblock %} + +{% block content %} + +
+ +
+
+
+ +

Unleash the power of GPU: Ubuntu WorkSpaces now support Graphics G4dn bundles

+
+

A few months ago, the OpenSSL Project announced the end of life of OpenSSL + 1.1.1. It is used by thousands of software components included in Ubuntu 18.04 LTS and Ubuntu 20.04 LTS, with + many organisations relying on version 1.1.1....

+ +
+
+ +

Canonical joins SOAFEE SIG Ubuntu Desktop 23.10:Mantic Minotaur deep dive

+
+

If you've been following my previous blog posts, you already know that + the automotive industry is changing faster than ever. Updating software and firmware in vehicles is one area + that's particularly challenging and in flux....

+ +
+
+ +

Canonical joins SOAFEE SIG Ubuntu Desktop 23.10:Mantic Minotaur deep dive

+
+

Today, around 96% of software projects utilize open source in some way. The + web team here at Canonical is passionate about Open source. We lead with an open-by-default approach and so + almost everything we do and work on can be found publicly on the Canonical Github org.

+ +
+
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/templates/docs/examples/patterns/equal-height-row/4-items-per-column.html b/templates/docs/examples/patterns/equal-height-row/4-items-per-column.html new file mode 100644 index 0000000000..1f0461052c --- /dev/null +++ b/templates/docs/examples/patterns/equal-height-row/4-items-per-column.html @@ -0,0 +1,100 @@ +{% extends "_layouts/examples.html" %} +{% block title %}Equal height row / Four items per column and full-screen divider{% endblock %} + +{% block standalone_css %}patterns_equal-height-row{% endblock %} + +{% block content %} + +
+
+
+
+ +
+
+
+
+
Continuous security
+
+

+ Imagine a world where every device is trustworthy. We designed every aspect of Ubuntu + Core to create the most secure embedded Linux ever, with a 10 year LTS commitment. +

+ +
+
+
+
+ +
+
+
+
+
Fleet management
+
+

+ Enjoy reliable, remotely accessible and recoverable devices which are always + up-to-date with mission-critical OTA updates. In connected or air-gap environments. +

+ +
+
+
+
+ +
+
+
+
+
Agile containerisation
+
+

+ Ubuntu Core is immutable and strictly confined. There is a clean separation between + the kernel, OS image and applications, each updated independently and protected + against corruption. +

+ +
+
+
+
+ +
+
+
+
+
A strong hardware ecosystem
+
+

+ We enable Ubuntu Core with the best ODMs and silicon vendors in the world. We + continuously test it on leading IoT and edge devices and hardware. +

+ +
+
+ +{% endblock %} \ No newline at end of file diff --git a/templates/docs/examples/patterns/equal-height-row/default.html b/templates/docs/examples/patterns/equal-height-row/default.html new file mode 100644 index 0000000000..1af7b15f11 --- /dev/null +++ b/templates/docs/examples/patterns/equal-height-row/default.html @@ -0,0 +1,73 @@ +{% extends "_layouts/examples.html" %} +{% block title %}Equal height row / Four column row{% endblock %} + +{% block standalone_css %}patterns_equal-height-row{% endblock %} + +{% block content %} + +
+
+
+
+ +
+
+

Use the Ubuntu terminal and run Linux applications on Windows. Use the Ubuntu + terminal + and run Linux applications on Windows.

+ +
+
+
+
+ +
+
+

Use your Raspberry Pi as a desktop, server or IoT device with Ubuntu.

+ +
+
+
+
+ +
+
+

Spin up Ubuntu VMs on Windows, Mac and Linux.

+ +
+
+
+
+ +
+
+

Fast, dense, and secure container and VM management at any scale.

+ +
+
+ +{% endblock %} \ No newline at end of file diff --git a/templates/docs/patterns/equal-height-row.md b/templates/docs/patterns/equal-height-row.md new file mode 100644 index 0000000000..538aa7e92d --- /dev/null +++ b/templates/docs/patterns/equal-height-row.md @@ -0,0 +1,90 @@ +--- +wrapper_template: '_layouts/docs.html' +context: + title: Equal height row | Components +--- + +The equal height row component aims to provide consistent alignment for grid items within a row format. This is achieved using the CSS `subgrid` feature which allows column grids to share the same layout used in the parent row grid. + +In addition to enforcing column grid items alignment within each row, responsive behaviour is provided out of the box as shown below: + + + + + + + + + + + + + + + + + + + + + + +
Screen size (px)Behaviour
Less than $breakpoint-smallEach column spans across the entire parent grid. Column items are vertically stacked.
$breakpoint-small - $breakpoint-largeEach column spans across the entire parent grid. The first item within each column is placed on the left of the other column items.
Greater than $breakpoint-largeColumns within the row are displayed horizontally. Column items are vertically stacked.
+ +This component is an extension of the [grid component](/docs/patterns/grid). There are several usage variations detailed in the examples below. + +## Full example + +Each column of the component can have up to 4 sub-grid items that will keep equal heights between the columns. + + + +### Cross-column dividers + +You can also insert dividers that span across all columns within a row using `.has-1st-divider`, `.has-2nd-divider` and `.has-3rd-divider`. + +
+
+
Note:
+

For smaller screen sizes i.e. < $breakpoint-large the divider will appear below relevant items within each column.

+
+
+ +
+
+

You may only have two dividers maximum. For example, if you have .has-1st-divider and .has-2nd-divider set for the row, then .has-3rd-divider will not be shown. This is a limitation due to usage of pseudo elements for visualising dividers that is capable of spanning across grid gaps.

+
+
+ +## Three column row + +You may use the equal height row component nested inside the 25/75 grid split pattern on large screen size (`.row--25-75-on-large`). The row (`.p-equal-height-row`) should be placed within the 75% container with a maximum of three columns. + + + +## Four column row + +Used as a four column row grid (`.p-equal-height-row`) spanning the whole width of the default page grid. Each column (`.p-equal-height-row__col`) within the component is a sub-grid and may have up to four items (`.p-equal-height-row__item`). + + + +## Import + +To import just this component into your project, copy the snippet below and include it in your main Sass file. + +```scss +// import Vanilla and include base mixins +// this only needs to happen once in a given project +@import 'vanilla-framework'; +@include vf-base; + +@include vf-p-equal-height-row; +``` + +For more information see [Customising Vanilla](/docs/customising-vanilla/) in your projects, which includes overrides and importing instructions.