diff --git a/releases.yml b/releases.yml
index 2aed37464..9ea75dec5 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 000000000..86cf518b5
--- /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 10cb58efb..bfd37d3e6 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 ae5e5499e..e290cf0e6 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 000000000..6be926283
--- /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 8a42c7e09..9a5658213 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 000000000..2e109ac11
--- /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....
+Learn more
+ +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....
+Learn more
+ +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.
+Learn more
+ ++ 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. +
+ ++ Enjoy reliable, remotely accessible and recoverable devices which are always + up-to-date with mission-critical OTA updates. In connected or air-gap environments. +
+ ++ 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. +
+ ++ 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. +
+ +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.
+ +Fast, dense, and secure container and VM management at any scale.
+Learn about LXD
+Screen size (px) | +Behaviour | +
---|---|
Less than $breakpoint-small |
+ Each column spans across the entire parent grid. Column items are vertically stacked. | +
$breakpoint-small - $breakpoint-large |
+ Each 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-large |
+ Columns within the row are displayed horizontally. Column items are vertically stacked. | +