From 5a1cbe6781eba249bee1669cd2246621166cfa8e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 6 Apr 2026 16:57:39 +0300 Subject: [PATCH] Add responsive adaptiveness for IoT Hub home, browse, and search pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add MediaBreakpoints lt-xmd (≤1599), gt-xxl (≥2448) to constants - Home: BreakpointObserver dynamically adjusts card counts per breakpoint - Home: main-content container with 48px gap and responsive padding - Home: hero icons positions/sizes adapt per breakpoint, hidden below ≤959px - Home: subtitle line break after Solution Templates - Home: installed button centered at ≤599px - Browse: responsive card grid with compact type support (CF/RC/DEVICE) - Search: responsive grid columns matching home page breakpoints - Card component: min-width 0 for grid overflow prevention - isCompactType includes DEVICE across all components --- .../iot-hub/iot-hub-browse.component.html | 2 +- .../iot-hub/iot-hub-browse.component.scss | 42 ++- .../pages/iot-hub/iot-hub-browse.component.ts | 4 + .../pages/iot-hub/iot-hub-home.component.html | 252 +++++++++--------- .../pages/iot-hub/iot-hub-home.component.scss | 178 ++++++++----- .../pages/iot-hub/iot-hub-home.component.ts | 69 ++++- .../iot-hub/iot-hub-item-card.component.scss | 1 + .../iot-hub/iot-hub-search.component.scss | 31 ++- .../pages/iot-hub/iot-hub-search.component.ts | 2 +- ui-ngx/src/app/shared/models/constants.ts | 3 + ui-ngx/src/scss/constants.scss | 2 + 11 files changed, 380 insertions(+), 206 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-browse.component.html b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-browse.component.html index 09c181e8ed..59557f53de 100644 --- a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-browse.component.html +++ b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-browse.component.html @@ -202,7 +202,7 @@ @if (!isLoading && !hasError && items.length > 0) { -
+
@for (item of items; track item.id) { {{ 'iot-hub.home-title' | translate }}
{{ 'iot-hub.home-subtitle-prefix' | translate }} - @for (ht of heroTypes; track ht.type; let last = $last) { + @for (ht of heroTypes; track ht.type; let i = $index; let last = $last) { {{ ht.labelKey | translate }}@if (!last) {,} + @if (i === 2) { + + } }
@@ -131,134 +134,145 @@
- -
- @for (card of categoryCards; track card.type) { -
- {{ card.titleKey | translate }} - -
- } -
+ +
+ +
+ @for (card of categoryCards; track card.type) { +
+ {{ card.titleKey | translate }} + +
+ } +
- @if (!isLoading) { - - @if (popularWidgets.length) { -
- -
- @for (item of popularWidgets; track item.id) { - - - } + @if (!isLoading) { + + @if (popularWidgets.length) { +
+ +
+ @for (item of popularWidgets; track item.id) { + + + } +
-
- } + } - - @if (popularDashboards.length) { -
- -
- @for (item of popularDashboards; track item.id) { - - - } + + @if (popularDashboards.length) { +
+ +
+ @for (item of popularDashboards; track item.id) { + + + } +
-
- } + } - - @if (popularSolutionTemplates.length) { -
- -
- @for (item of popularSolutionTemplates; track item.id) { - - - } + + @if (popularSolutionTemplates.length) { +
+ +
+ @for (item of popularSolutionTemplates; track item.id) { + + + } +
-
- } + } - - @if (popularCalcFields.length) { -
- -
- @for (item of popularCalcFields; track item.id) { - - - } + + @if (popularCalcFields.length) { +
+ +
+ @for (item of popularCalcFields; track item.id) { + + + } +
-
- } + } - - @if (popularRuleChains.length) { -
- -
- @for (item of popularRuleChains; track item.id) { - - - } + + @if (popularRuleChains.length) { +
+ +
+ @for (item of popularRuleChains; track item.id) { + + + } +
+ } + } + + @if (isLoading) { +
+
} +
- + + @if (!isLoading) {

{{ 'iot-hub.become-a-creator' | translate }}

@@ -268,10 +282,4 @@
} - - @if (isLoading) { -
- -
- }
diff --git a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.scss b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.scss index b81f104aee..7251f41187 100644 --- a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.scss +++ b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.scss @@ -102,6 +102,7 @@ } // Individual floating icon — SVG has rotation/bg/shadow baked in, CSS handles position + animation +// Base: ≥1600px — 78px top icons (top: 40px), 87px bottom icons (top: 216px, bottom edge: 303px) .tb-iot-hub-hero-float-icon { position: absolute; width: 78px; @@ -116,35 +117,37 @@ // Top-left &.icon-pos-1 { top: 40px; - left: 23%; - --icon-rotation: ; + left: calc(50% - 404px); --start-x: 40px; --start-y: 12px; } - // Top-right + // Top-right — mirrors icon-pos-1 &.icon-pos-2 { top: 40px; - right: 23%; - --icon-rotation: ; + left: auto; + right: calc(50% - 404px); --start-x: -40px; --start-y: 12px; } - // Bottom-left + // Bottom-left — 87px bounding box &.icon-pos-3 { - bottom: 100px; - left: 18%; - --icon-rotation: ; + top: 216px; + left: calc(50% - 487px); + width: 87px; + height: 87px; --start-x: 50px; --start-y: -8px; } - // Bottom-right + // Bottom-right — 87px bounding box, mirrors icon-pos-3 &.icon-pos-4 { - bottom: 100px; - right: 18%; - --icon-rotation: ; + top: 216px; + left: auto; + right: calc(50% - 487px); + width: 87px; + height: 87px; --start-x: -45px; --start-y: -4px; } @@ -178,6 +181,11 @@ gap: 6px; } +.tb-iot-hub-hero-subtitle-break { + flex-basis: 100%; + height: 0; +} + .tb-iot-hub-hero-prefix { font-weight: 400; color: rgba(0, 0, 0, 0.76); @@ -425,19 +433,26 @@ } } -// Category cards — Design: centered 1200px container, flex-col gap=20, pb=48, 3-col rows +// Main content — wraps categories + item sections +.tb-iot-hub-main-content { + display: flex; + flex-direction: column; + gap: 48px; + padding: 0 40px 64px; +} + +// Category cards — Design: centered 1200px container, 3-col rows .tb-iot-hub-categories { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; max-width: 1200px; margin: 0 auto; - padding-bottom: 48px; + width: 100%; } // Card — Design: flex-col, gap=24, pt=24, rounded-8, border, overflow-clip, h=220 .tb-iot-hub-category-card { - height: 220px; border-radius: 8px; cursor: pointer; display: flex; @@ -470,8 +485,7 @@ // Card image — Design: h=148, w=full, overflow-clip, fills remaining space .tb-iot-hub-category-img { width: 100%; - height: 148px; - object-fit: cover; + object-fit: contain; flex-shrink: 0; } @@ -500,10 +514,8 @@ background: linear-gradient(104.75deg, rgb(245, 255, 252) 0%, rgb(147, 240, 213) 100%); } -// Sections — matches Design "items-section" (px=40, gap between sections ~48px) +// Sections — inside main-content, no individual padding needed .tb-iot-hub-section { - padding: 0 40px; - margin-top: 48px; } // Section header — mat-button, Design: height=40, font-size=20, font-weight=500 @@ -541,7 +553,6 @@ .tb-iot-hub-divider { height: 1px; background: rgba(0, 0, 0, 0.12); - margin: 48px 0 0; } // Become a Creator — matches Design (pt=64, text centered, button at y=156) @@ -606,23 +617,41 @@ animation: iot-hub-fade-slide-up 1s cubic-bezier(0.19, 1, 0.22, 1) 0.2s both; } -// Responsive — breakpoints from design: LG=1280, MD=960, SM=600, XS=375 +// Responsive — breakpoints from design: XXXL=2448, XXL=1920, XL=1600, LG=1280, MD=960, SM=600, XS=375 -// LG: 1280px content area (sidebar present) -@media (max-width: 1600px) { +// ≥2448px: 6-col big cards, 4-col compact +@media #{$mat-gt-xxl} { .tb-iot-hub-big-cards-row { + grid-template-columns: repeat(6, 1fr); + } + + .tb-iot-hub-compact-cards-grid { grid-template-columns: repeat(4, 1fr); } +} + +// ≤1919px: 4-col big cards, 3-col compact +@media #{$mat-lt-xl} { + .tb-iot-hub-big-cards-row { + grid-template-columns: repeat(4, 1fr); + } +} + +// ≤1599px: 3-col big cards, 2-col compact +@media #{$mat-lt-xmd} { + .tb-iot-hub-big-cards-row { + grid-template-columns: repeat(3, 1fr); + } .tb-iot-hub-compact-cards-grid { grid-template-columns: repeat(2, 1fr); } } -// MD: 960px viewport — sidebar present, narrower content -@media (max-width: 1280px) { +// ≤1279px: 2-col big cards, 1-col compact, 2-col categories, smaller icons +@media #{$mat-lt-lg} { .tb-iot-hub-big-cards-row { - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(2, 1fr); } .tb-iot-hub-compact-cards-grid { @@ -631,14 +660,10 @@ .tb-iot-hub-categories { grid-template-columns: repeat(2, 1fr); - - .tb-iot-hub-category-card { - height: 189px; - } } .tb-iot-hub-hero { - padding: 100px 24px 60px; + padding: 140px 24px 60px; } .tb-iot-hub-hero-title { @@ -650,75 +675,102 @@ line-height: 22px; } - .tb-iot-hub-section { - padding: 0 24px; + .tb-iot-hub-main-content { + padding: 0 24px 64px; } .tb-iot-hub-become-creator { padding: 64px 24px; } -} -// SM: 600px viewport — sidebar hidden, full width -@media (max-width: 960px) { - .tb-iot-hub-big-cards-row { - grid-template-columns: repeat(2, 1fr); - } + // Icons: ≥1280px — 69px top (top: 90px), 77px bottom (top: 246px, bottom edge: 323px) + .tb-iot-hub-hero-float-icon { + width: 69px; + height: 69px; - .tb-iot-hub-categories { - .tb-iot-hub-category-card { - height: 168px; + &.icon-pos-1 { + top: 90px; + left: 106px; + } + + &.icon-pos-2 { + top: 90px; + left: auto; + right: 106px; + } + + &.icon-pos-3 { + top: 246px; + left: 28px; + width: 77px; + height: 77px; + } + + &.icon-pos-4 { + top: 246px; + left: auto; + right: 28px; + width: 77px; + height: 77px; } } +} +// ≤959px: only top pair, area width ≤160px (icon 51px + gap + icon 51px) +@media #{$mat-lt-md} { .tb-iot-hub-hero-float-icon { width: 51px; height: 51px; - img { - width: 51px; - height: 51px; + &.icon-pos-1 { + top: 80px; + left: calc(50% - 80px); + } + + &.icon-pos-2 { + top: 80px; + left: auto; + right: calc(50% - 80px); + } + + &.icon-pos-3, + &.icon-pos-4 { + display: none; } } } -// XS: 375px viewport — single column, stacked layout -@media (max-width: 600px) { +// ≤599px: only top pair visible (51px), bottom pair hidden +@media #{$mat-lt-sm} { :host { padding: 8px; } .tb-iot-hub-hero { - padding: 80px 16px 40px; + padding: 140px 16px 40px; } .tb-iot-hub-categories { grid-template-columns: 1fr; - padding-bottom: 32px; - - .tb-iot-hub-category-card { - height: 197px; - } } .tb-iot-hub-big-cards-row { - grid-template-columns: repeat(2, 1fr); + grid-template-columns: 1fr; } - .tb-iot-hub-section { - padding: 0 16px; + .tb-iot-hub-main-content { + padding: 0 16px 48px; } .tb-iot-hub-become-creator { padding: 48px 16px; } - .tb-iot-hub-hero-float-icon { - display: none; - } - .tb-iot-hub-installed-btn { top: 12px; - right: 12px; + right: auto; + left: 50%; + transform: translateX(-50%); } + } diff --git a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts index 6cb6777a49..00f49903b4 100644 --- a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts +++ b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts @@ -19,8 +19,10 @@ import { Router } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; +import { BreakpointObserver } from '@angular/cdk/layout'; import { forkJoin, Subject, Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; +import { MediaBreakpoints } from '@shared/models/constants'; import { PageLink } from '@shared/models/page/page-link'; import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { MpItemVersionQuery, MpItemVersionView } from '@shared/models/iot-hub/iot-hub-version.models'; @@ -134,15 +136,35 @@ export class TbIotHubHomeComponent implements OnInit, OnDestroy { isLoading = true; + bigCardCount = 5; + compactCardCount = 6; + private breakpointSubscription: Subscription; + constructor( private router: Router, private dialog: MatDialog, private iotHubApiService: IotHubApiService, - private translate: TranslateService + private translate: TranslateService, + private breakpointObserver: BreakpointObserver ) {} ngOnInit(): void { + this.updateCardCounts(); this.loadPopularItems(); + this.breakpointSubscription = this.breakpointObserver.observe([ + MediaBreakpoints['lt-sm'], + MediaBreakpoints['lt-md'], + MediaBreakpoints['lt-lg'], + MediaBreakpoints['lt-xmd'], + MediaBreakpoints['lt-xl'], + MediaBreakpoints['gt-xxl'] + ]).subscribe(() => { + const prev = { big: this.bigCardCount, compact: this.compactCardCount }; + this.updateCardCounts(); + if (this.bigCardCount !== prev.big || this.compactCardCount !== prev.compact) { + this.loadPopularItems(); + } + }); this.searchSubscription = this.searchSubject.pipe( debounceTime(300), distinctUntilChanged(), @@ -169,6 +191,7 @@ export class TbIotHubHomeComponent implements OnInit, OnDestroy { ngOnDestroy(): void { this.stopHeroCycle(); this.searchSubscription?.unsubscribe(); + this.breakpointSubscription?.unsubscribe(); } onHeroTypeHover(config: HeroTypeConfig): void { @@ -236,7 +259,7 @@ export class TbIotHubHomeComponent implements OnInit, OnDestroy { } isCompactType(type: ItemType): boolean { - return type === ItemType.CALCULATED_FIELD || type === ItemType.RULE_CHAIN; + return type === ItemType.CALCULATED_FIELD || type === ItemType.RULE_CHAIN || type === ItemType.DEVICE; } getCompactIcon(item: MpItemVersionView): string { @@ -438,6 +461,38 @@ export class TbIotHubHomeComponent implements OnInit, OnDestroy { window.open('https://iothub.thingsboard.io/signup', '_blank'); } + private updateCardCounts(): void { + if (this.breakpointObserver.isMatched(MediaBreakpoints['lt-sm'])) { + // ≤599px: 1-col big cards, 1-col compact + this.bigCardCount = 2; + this.compactCardCount = 4; + } else if (this.breakpointObserver.isMatched(MediaBreakpoints['lt-md'])) { + // ≤959px: 2-col big cards, 1-col compact + this.bigCardCount = 4; + this.compactCardCount = 4; + } else if (this.breakpointObserver.isMatched(MediaBreakpoints['lt-lg'])) { + // ≤1279px: 2-col big cards, 1-col compact + this.bigCardCount = 4; + this.compactCardCount = 4; + } else if (this.breakpointObserver.isMatched(MediaBreakpoints['lt-xmd'])) { + // ≤1599px: 3-col big cards, 2-col compact + this.bigCardCount = 3; + this.compactCardCount = 4; + } else if (this.breakpointObserver.isMatched(MediaBreakpoints['lt-xl'])) { + // ≤1919px: 4-col big cards, 3-col compact + this.bigCardCount = 4; + this.compactCardCount = 6; + } else if (this.breakpointObserver.isMatched(MediaBreakpoints['gt-xxl'])) { + // ≥2448px: 6-col big cards, 4-col compact + this.bigCardCount = 6; + this.compactCardCount = 8; + } else { + // ≥1920px: 5-col big cards, 3-col compact + this.bigCardCount = 5; + this.compactCardCount = 6; + } + } + private loadPopularItems(): void { const sortOrder: SortOrder = { property: 'totalInstallCount', direction: Direction.DESC }; const config = { ignoreLoading: true }; @@ -449,11 +504,11 @@ export class TbIotHubHomeComponent implements OnInit, OnDestroy { }; forkJoin({ - widgets: this.iotHubApiService.getPublishedVersions(buildQuery(ItemType.WIDGET, 5), config), - dashboards: this.iotHubApiService.getPublishedVersions(buildQuery(ItemType.DASHBOARD, 5), config), - solutionTemplates: this.iotHubApiService.getPublishedVersions(buildQuery(ItemType.SOLUTION_TEMPLATE, 5), config), - calcFields: this.iotHubApiService.getPublishedVersions(buildQuery(ItemType.CALCULATED_FIELD, 6), config), - ruleChains: this.iotHubApiService.getPublishedVersions(buildQuery(ItemType.RULE_CHAIN, 6), config), + widgets: this.iotHubApiService.getPublishedVersions(buildQuery(ItemType.WIDGET, this.bigCardCount), config), + dashboards: this.iotHubApiService.getPublishedVersions(buildQuery(ItemType.DASHBOARD, this.bigCardCount), config), + solutionTemplates: this.iotHubApiService.getPublishedVersions(buildQuery(ItemType.SOLUTION_TEMPLATE, this.bigCardCount), config), + calcFields: this.iotHubApiService.getPublishedVersions(buildQuery(ItemType.CALCULATED_FIELD, this.compactCardCount), config), + ruleChains: this.iotHubApiService.getPublishedVersions(buildQuery(ItemType.RULE_CHAIN, this.compactCardCount), config), installedWidgets: this.iotHubApiService.getInstalledItems(installedPageLink, ItemType.WIDGET, config), installedSolutionTemplates: this.iotHubApiService.getInstalledItems(installedPageLink, ItemType.SOLUTION_TEMPLATE, config), installedCount: this.iotHubApiService.getInstalledItemsCount(null, config) diff --git a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-card.component.scss b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-card.component.scss index 3e7d038e44..13bbcec662 100644 --- a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-card.component.scss +++ b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-card.component.scss @@ -19,6 +19,7 @@ :host { display: block; height: 100%; + min-width: 0; } // Card — Design: white bg, border rgba(0,0,0,0.12), rounded-8, overflow-clip, flex-col, gap-16, pb-16 diff --git a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-search.component.scss b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-search.component.scss index 4dbed2491e..24e5ab5fb7 100644 --- a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-search.component.scss +++ b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-search.component.scss @@ -214,7 +214,7 @@ } } -// Card grid — Design: 5-column for big cards, 3-column for compact +// Card grid — base: ≥1920 5-col big, 3-col compact .tb-search-card-grid { display: grid; grid-template-columns: repeat(5, 1fr); @@ -225,18 +225,29 @@ } } -// Responsive -@media (max-width: 1400px) { +// Responsive — same columns as iot-hub home + +@media #{$mat-gt-xxl} { + .tb-search-card-grid { + grid-template-columns: repeat(6, 1fr); + + &.tb-search-card-grid-compact { + grid-template-columns: repeat(4, 1fr); + } + } +} + +@media #{$mat-lt-xl} { .tb-search-card-grid { grid-template-columns: repeat(4, 1fr); &.tb-search-card-grid-compact { - grid-template-columns: repeat(2, 1fr); + grid-template-columns: repeat(3, 1fr); } } } -@media (max-width: 1100px) { +@media #{$mat-lt-xmd} { .tb-search-card-grid { grid-template-columns: repeat(3, 1fr); @@ -246,7 +257,7 @@ } } -@media (max-width: 900px) { +@media #{$mat-lt-lg} { .tb-search-card-grid { grid-template-columns: repeat(2, 1fr); @@ -254,7 +265,9 @@ grid-template-columns: repeat(1, 1fr); } } +} +@media #{$mat-lt-md} { .tb-search-input-field { width: 100%; flex: 1; @@ -264,3 +277,9 @@ padding: 24px; } } + +@media #{$mat-lt-sm} { + .tb-search-card-grid { + grid-template-columns: repeat(1, 1fr); + } +} diff --git a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-search.component.ts b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-search.component.ts index 039c1f25c8..b1b3252b7b 100644 --- a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-search.component.ts +++ b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-search.component.ts @@ -173,7 +173,7 @@ export class TbIotHubSearchComponent implements OnInit, OnDestroy { // Type helpers isCompactType(type: ItemType): boolean { - return type === ItemType.CALCULATED_FIELD || type === ItemType.RULE_CHAIN; + return type === ItemType.CALCULATED_FIELD || type === ItemType.RULE_CHAIN || type === ItemType.DEVICE; } getTypeLabel(type: ItemType): string { diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 429bfe217a..1117f032ed 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -69,11 +69,14 @@ export const MediaBreakpoints = { 'lt-sm': 'screen and (max-width: 599px)', 'lt-md': 'screen and (max-width: 959px)', 'lt-lg': 'screen and (max-width: 1279px)', + 'lt-xmd': 'screen and (max-width: 1599px)', 'lt-xl': 'screen and (max-width: 1919px)', 'gt-xs': 'screen and (min-width: 600px)', 'gt-sm': 'screen and (min-width: 960px)', 'gt-md': 'screen and (min-width: 1280px)', + 'gt-xmd': 'screen and (min-width: 1600px)', 'gt-lg': 'screen and (min-width: 1920px)', + 'gt-xxl': 'screen and (min-width: 2448px)', 'gt-xl': 'screen and (min-width: 5001px)', 'md-lg': 'screen and (min-width: 960px) and (max-width: 1819px)' }; diff --git a/ui-ngx/src/scss/constants.scss b/ui-ngx/src/scss/constants.scss index aca02be591..83c7114f01 100644 --- a/ui-ngx/src/scss/constants.scss +++ b/ui-ngx/src/scss/constants.scss @@ -25,7 +25,9 @@ $mat-lt-xl: "screen and (max-width: 1919px)"; $mat-gt-xs: "screen and (min-width: 600px)"; $mat-gt-sm: "screen and (min-width: 960px)"; $mat-gt-md: "screen and (min-width: 1280px)"; +$mat-lt-xmd: "screen and (max-width: 1599px)"; $mat-gt-xmd: "screen and (min-width: 1600px)"; +$mat-gt-xxl: "screen and (min-width: 2448px)"; $mat-gt-xl: "screen and (min-width: 1920px)"; $mat-md-lg: "screen and (min-width: 960px) and (max-width: 1819px)";