From d6cfffb4dfd6ac290d179713356777f322165fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kuna?= <1282324+codecalm@users.noreply.github.com> Date: Thu, 8 Oct 2026 14:20:34 +0200 Subject: [PATCH] Add Mockup browser component (#3276) --- .changeset/mockup-browser.md | 7 + core/scss/_core.scss | 1 + core/scss/_variables-dark.scss | 1 + core/scss/_variables.scss | 28 ++++ core/scss/layout/_root.scss | 3 +- core/scss/ui/_mockup-browser.scss | 111 +++++++++++++ docs/content/ui/components/mockup-browser.mdx | 152 ++++++++++++++++++ preview/pages/mockup-browser.astro | 118 ++++++++++++++ screenshots/pages/mockup-browser.astro | 13 ++ shared/data/docs.json | 4 + shared/data/menu.json | 5 + shared/ui/MockupBrowser.astro | 37 +++++ 12 files changed, 479 insertions(+), 1 deletion(-) create mode 100644 .changeset/mockup-browser.md create mode 100644 core/scss/ui/_mockup-browser.scss create mode 100644 docs/content/ui/components/mockup-browser.mdx create mode 100644 preview/pages/mockup-browser.astro create mode 100644 screenshots/pages/mockup-browser.astro create mode 100644 shared/ui/MockupBrowser.astro diff --git a/.changeset/mockup-browser.md b/.changeset/mockup-browser.md new file mode 100644 index 000000000..d94f4a322 --- /dev/null +++ b/.changeset/mockup-browser.md @@ -0,0 +1,7 @@ +--- +'@tabler/core': minor +'@tabler/preview': minor +'@tabler/docs': minor +--- + +Added the mockup browser component: `.mockup-browser` wraps a screenshot or any content in a frame that looks like a browser window, with window dots and an address bar. diff --git a/core/scss/_core.scss b/core/scss/_core.scss index 2c69bf584..6218ad90e 100644 --- a/core/scss/_core.scss +++ b/core/scss/_core.scss @@ -76,6 +76,7 @@ @forward 'ui/lists'; @forward 'ui/loaders'; @forward 'ui/login'; +@forward 'ui/mockup-browser'; @forward 'ui/modals'; @forward 'ui/nav'; @forward 'ui/nav-overflow'; diff --git a/core/scss/_variables-dark.scss b/core/scss/_variables-dark.scss index 6b478bf5d..df7d719bc 100644 --- a/core/scss/_variables-dark.scss +++ b/core/scss/_variables-dark.scss @@ -9,6 +9,7 @@ $darken-dark: adjust-lightness-css(var(--dark), -2%) !default; $border-color-dark: adjust-lightness-css(var(--dark), 8%) !default; $border-color-translucent-dark: rgba(128, 150, 172, 0.2) !default; $border-dark-color-dark: adjust-lightness-css(var(--dark), 4%) !default; +$border-light-color-dark: adjust-lightness-css(var(--dark), 6%) !default; $border-active-color-dark: adjust-lightness-css(var(--dark), 12%) !default; //new bootsrap variables diff --git a/core/scss/_variables.scss b/core/scss/_variables.scss index ba31bb158..1ab7ebada 100644 --- a/core/scss/_variables.scss +++ b/core/scss/_variables.scss @@ -1794,6 +1794,34 @@ $text-shimmer-loader-duration: 0.75s !default; $text-shimmer-loader-opacity: 0.25 !default; // scss-docs-end text-shimmer-variables +// Mockup browser +// scss-docs-start mockup-browser-variables +$mockup-browser-bg: var(--bg-surface) !default; +$mockup-browser-border-color: var(--border-light-color) !default; +$mockup-browser-border-radius: var(--border-radius-lg) !default; +$mockup-browser-box-shadow: var(--shadow-lg) !default; +$mockup-browser-toolbar-min-height: 2rem !default; +$mockup-browser-toolbar-padding-y: 0.5rem !default; +$mockup-browser-toolbar-padding-x: 12.5% !default; +$mockup-browser-toolbar-color: var(--secondary) !default; +$mockup-browser-toolbar-bg: var(--bg-surface-secondary) !default; +$mockup-browser-control-offset: 1rem !default; +$mockup-browser-control-size: 0.75rem !default; +$mockup-browser-control-gap: 0.5rem !default; +$mockup-browser-control-colors: #ff5f57, #febc2e, #28c840 !default; +$mockup-browser-url-font-size: $h5-font-size !default; +$mockup-browser-url-line-height: 1rem !default; +$mockup-browser-url-icon-size: 0.875rem !default; + +$mockup-browser-sm-border-radius: var(--border-radius) !default; +$mockup-browser-sm-toolbar-min-height: 1.5rem !default; +$mockup-browser-sm-toolbar-padding-y: 0.25rem !default; +$mockup-browser-sm-control-size: 0.5rem !default; +$mockup-browser-sm-control-gap: 0.25rem !default; +$mockup-browser-sm-url-font-size: $h6-font-size !default; +$mockup-browser-sm-url-icon-size: 0.75rem !default; +// scss-docs-end mockup-browser-variables + // Steps // scss-docs-start steps-variables $steps-border-width: 2px !default; diff --git a/core/scss/layout/_root.scss b/core/scss/layout/_root.scss index 139a6c335..eea0b9ccc 100644 --- a/core/scss/layout/_root.scss +++ b/core/scss/layout/_root.scss @@ -43,7 +43,7 @@ --border-color-translucent: light-dark(#{$border-color-translucent}, var(--dark-mode-border-color-translucent)); --border-dark-color: light-dark(#{$border-dark-color}, var(--dark-mode-border-dark-color)); --border-dark-color-translucent: #{$border-dark-color-translucent}; - --border-light-color: #{$border-light-color}; + --border-light-color: light-dark(#{$border-light-color}, var(--dark-mode-border-light-color)); --border-light-color-translucent: #{$border-light-color-translucent}; --border-active-color: light-dark(#{$border-active-color}, var(--dark-mode-border-active-color)); --border-active-color-translucent: #{$border-active-color-translucent}; @@ -65,6 +65,7 @@ --dark-mode-border-color-translucent: #{$border-color-translucent-dark}; --dark-mode-border-active-color: #{$border-active-color-dark}; --dark-mode-border-dark-color: #{$border-dark-color-dark}; + --dark-mode-border-light-color: #{$border-light-color-dark}; --page-padding: #{$page-padding}; --page-padding-y: #{$page-padding-y}; diff --git a/core/scss/ui/_mockup-browser.scss b/core/scss/ui/_mockup-browser.scss new file mode 100644 index 000000000..c89cee697 --- /dev/null +++ b/core/scss/ui/_mockup-browser.scss @@ -0,0 +1,111 @@ +@use 'sass:list'; +@use '../config' as *; + +// A frame that looks like a browser window, to show a page preview or a screenshot +.mockup-browser { + // scss-docs-start mockup-browser-css-vars + --mockup-browser-bg: #{$mockup-browser-bg}; + --mockup-browser-border-color: #{$mockup-browser-border-color}; + --mockup-browser-border-radius: #{$mockup-browser-border-radius}; + --mockup-browser-box-shadow: #{$mockup-browser-box-shadow}; + --mockup-browser-toolbar-min-height: #{$mockup-browser-toolbar-min-height}; + --mockup-browser-toolbar-padding-y: #{$mockup-browser-toolbar-padding-y}; + --mockup-browser-toolbar-padding-x: #{$mockup-browser-toolbar-padding-x}; + --mockup-browser-toolbar-color: #{$mockup-browser-toolbar-color}; + --mockup-browser-toolbar-bg: #{$mockup-browser-toolbar-bg}; + --mockup-browser-control-offset: #{$mockup-browser-control-offset}; + --mockup-browser-control-size: #{$mockup-browser-control-size}; + --mockup-browser-control-gap: #{$mockup-browser-control-gap}; + --mockup-browser-control-color-1: #{list.nth($mockup-browser-control-colors, 1)}; + --mockup-browser-control-color-2: #{list.nth($mockup-browser-control-colors, 2)}; + --mockup-browser-control-color-3: #{list.nth($mockup-browser-control-colors, 3)}; + --mockup-browser-url-font-size: #{$mockup-browser-url-font-size}; + --mockup-browser-url-line-height: #{$mockup-browser-url-line-height}; + --mockup-browser-url-icon-size: #{$mockup-browser-url-icon-size}; + // scss-docs-end mockup-browser-css-vars + display: flex; + flex-direction: column; + overflow: hidden; + background: var(--mockup-browser-bg); + border: var(--border-width) solid var(--mockup-browser-border-color); + box-shadow: var(--mockup-browser-box-shadow); + @include border-radius(var(--mockup-browser-border-radius)); +} + +// Small: a 24px toolbar for thumbnails and cards +.mockup-browser-sm { + --mockup-browser-border-radius: #{$mockup-browser-sm-border-radius}; + --mockup-browser-toolbar-min-height: #{$mockup-browser-sm-toolbar-min-height}; + --mockup-browser-toolbar-padding-y: #{$mockup-browser-sm-toolbar-padding-y}; + --mockup-browser-control-size: #{$mockup-browser-sm-control-size}; + --mockup-browser-control-gap: #{$mockup-browser-sm-control-gap}; + --mockup-browser-url-font-size: #{$mockup-browser-sm-url-font-size}; + --mockup-browser-url-icon-size: #{$mockup-browser-sm-url-icon-size}; +} + +// The top bar: the window dots sit at the start, the address bar fills the middle +.mockup-browser-toolbar { + position: relative; + display: flex; + align-items: center; + min-height: var(--mockup-browser-toolbar-min-height); + padding: var(--mockup-browser-toolbar-padding-y) var(--mockup-browser-toolbar-padding-x); + color: var(--mockup-browser-toolbar-color); + background: var(--mockup-browser-toolbar-bg); + // Drawn as an inset shadow so the line does not add to the toolbar height + box-shadow: inset 0 calc(-1 * var(--border-width)) var(--mockup-browser-border-color); +} + +// The three window dots, drawn with one element and two shadows. They are taken +// out of the flow so the address bar stays centered whatever the frame width +.mockup-browser-controls { + position: absolute; + inset-inline-start: var(--mockup-browser-control-offset); + width: var(--mockup-browser-control-size); + height: var(--mockup-browser-control-size); + background: var(--mockup-browser-control-color-1); + border-radius: 50%; + box-shadow: + calc(var(--mockup-browser-control-size) + var(--mockup-browser-control-gap)) 0 0 var(--mockup-browser-control-color-2), + calc(2 * (var(--mockup-browser-control-size) + var(--mockup-browser-control-gap))) 0 0 var(--mockup-browser-control-color-3); +} + +// The address, plain centered text +.mockup-browser-url { + display: flex; + gap: 0.25rem; + align-items: center; + justify-content: center; + width: 100%; + min-width: 0; + font-size: var(--mockup-browser-url-font-size); + line-height: var(--mockup-browser-url-line-height); + + > span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .icon { + --icon-size: var(--mockup-browser-url-icon-size); + flex-shrink: 0; + } +} + +// The page: a screenshot fills the width, other content keeps its own layout +.mockup-browser-body { + flex: 1; + min-height: 0; + + > img, + > iframe, + > video { + display: block; + width: 100%; + } + + > iframe { + border: 0; + } +} diff --git a/docs/content/ui/components/mockup-browser.mdx b/docs/content/ui/components/mockup-browser.mdx new file mode 100644 index 000000000..71c9483d1 --- /dev/null +++ b/docs/content/ui/components/mockup-browser.mdx @@ -0,0 +1,152 @@ +--- +title: Mockup browser +summary: A mockup browser is a frame that looks like a browser window, with the window dots and an address bar, to show a page preview or a screenshot. +description: Show a screenshot or any content inside a browser window frame with the mockup browser component. Add an address, a lock icon or your own colors. +related: [/ui/components/image-label, /ui/components/card] +source: core/scss/ui/_mockup-browser.scss +css-vars: mockup-browser +classnames: + component: + - class: mockup-browser + desc: The frame, with a border, rounded corners and a shadow + size: + - class: mockup-browser-sm + desc: A 24px toolbar with smaller dots and text, for thumbnails and cards + part: + - class: mockup-browser-toolbar + desc: The top bar with the window dots and the address bar + - class: mockup-browser-controls + desc: The three window dots + - class: mockup-browser-url + desc: The address, centered in the toolbar + - class: mockup-browser-body + desc: The page area that holds the screenshot or the content +--- +import Example from '@components/Example.astro'; +import Icon from '@ui/Icon.astro'; + +## Default markup + +A `.mockup-browser` has a `.mockup-browser-toolbar` with the window dots and an address bar, and a `.mockup-browser-body` with the page. An image in the body fills the width of the frame. + + +
+
+ +
preview.tabler.io
+
+
+ Tabler dashboard + Tabler dashboard +
+
+
+ +## Secure address + +Put an icon before the address text to show a secure page. The icon is scaled down to match the text. + + +
+
+ +
https://preview.tabler.io
+
+
+ Tabler dashboard + Tabler dashboard +
+
+
+ +## Without an address bar + +Leave out `.mockup-browser-url` for a plain window frame with only the dots. + + +
+
+ +
+
+ Tabler dashboard + Tabler dashboard +
+
+
+ +## Sizes + +Add `.mockup-browser-sm` for a 24px toolbar with smaller dots and text. Use it for thumbnails and for frames inside cards. + + +
+
+
+
+ +
preview.tabler.io
+
+
+ Tabler dashboard + Tabler dashboard +
+
+
+
+
+
+ +
preview.tabler.io
+
+
+ Tabler dashboard + Tabler dashboard +
+
+
+
+
+ +## Any content + +The body takes any markup, not only an image. Add a padding utility to the body when the content needs space from the frame. + + +
+
+ +
example.com/signup
+
+
+

Create an account

+
+ + +
+ +
+
+
+ +## Custom look + +Set the `--tblr-mockup-browser-*` variables on the frame to change the colors, the shadow or the size of the dots. The three dots have their own color variables, `--tblr-mockup-browser-control-color-1` to `-3`. + + +
+
+ +
preview.tabler.io
+
+
+ Tabler dashboard +
+
+
+ +## Accessibility + +- The window dots are decoration. They are an empty element, so screen readers skip them. +- The address bar is plain text and is read as such. It is not a link and not a form field. +- Give a screenshot in the body an `alt` text that says what page it shows. diff --git a/preview/pages/mockup-browser.astro b/preview/pages/mockup-browser.astro new file mode 100644 index 000000000..314e76075 --- /dev/null +++ b/preview/pages/mockup-browser.astro @@ -0,0 +1,118 @@ +--- +import DefaultLayout from '@shared/layouts/DefaultLayout.astro' +import Card from '@ui/Card.astro' +import CardBody from '@ui/CardBody.astro' +import CardTitle from '@ui/CardTitle.astro' +import CardSubtitle from '@ui/CardSubtitle.astro' +import DocsLink from '@ui/DocsLink.astro' +import MockupBrowser from '@ui/MockupBrowser.astro' +import Button from '@ui/Button.astro' +--- + + + + +
+
+ + + Basic + A screenshot in a .mockup-browser frame, with the window dots and an address bar. + +
+
Your page goes here
+
+
+
+
+
+ +
+ + + Secure address + Put a lock icon before the address to show a secure page. + +
+
Your page goes here
+
+
+
+
+
+ +
+ + + Without an address bar + Leave out .mockup-browser-url for a plain window frame. + +
+
Your page goes here
+
+
+
+
+
+ +
+ + + Sizes + Add .mockup-browser-sm for a 24px toolbar with smaller dots and text, for thumbnails and cards. +
+
+ +
+
Your page goes here
+
+
+
+
+ +
+
Your page goes here
+
+
+
+
+
+
+
+ +
+ + + Any content + The body takes any markup, not only an image, so a component can be shown as a page. + +

Create an account

+
+ + +
+
+ + +
+
+ +
+ + + Custom look + Set the --tblr-mockup-browser-* variables for a dark toolbar, other window dot colors or no shadow. + +
+
Your page goes here
+
+
+
+
+
+
+
diff --git a/screenshots/pages/mockup-browser.astro b/screenshots/pages/mockup-browser.astro new file mode 100644 index 000000000..8a072a613 --- /dev/null +++ b/screenshots/pages/mockup-browser.astro @@ -0,0 +1,13 @@ +--- +// Mockup browser on its own, no card: one frame with the lock icon and the +// Tabler dashboard screenshot, light and dark through the hide-theme classes. +import ScreenshotLayout from '../layouts/ScreenshotLayout.astro' +import MockupBrowser from '@ui/MockupBrowser.astro' +--- + + + + + + + diff --git a/shared/data/docs.json b/shared/data/docs.json index 5a080d46c..028a03f95 100644 --- a/shared/data/docs.json +++ b/shared/data/docs.json @@ -254,6 +254,10 @@ "title": "Modal", "url": "/ui/components/modal" }, + { + "title": "Mockup browser", + "url": "/ui/components/mockup-browser" + }, { "title": "Nav overflow", "url": "/ui/components/nav-overflow" diff --git a/shared/data/menu.json b/shared/data/menu.json index 3e6448ca4..bf005cd37 100644 --- a/shared/data/menu.json +++ b/shared/data/menu.json @@ -190,6 +190,11 @@ "url": "modals.html", "title": "Modals" }, + "mockup-browser": { + "url": "mockup-browser.html", + "title": "Mockup browser", + "badge": "New" + }, "prose": { "title": "Prose", "url": "prose.html" diff --git a/shared/ui/MockupBrowser.astro b/shared/ui/MockupBrowser.astro new file mode 100644 index 000000000..e9090e043 --- /dev/null +++ b/shared/ui/MockupBrowser.astro @@ -0,0 +1,37 @@ +--- +// Mockup browser (.mockup-browser): a frame that looks like a browser window, +// with the window dots and an address bar above the content in the slot. + +interface Props { + /** text shown in the address bar; no address bar when omitted */ + url?: string | undefined + /** show a lock icon before the url */ + secure?: boolean + /** `.mockup-browser-sm`: a 24px toolbar for thumbnails and cards */ + size?: 'sm' + class?: string + /** class of the `.mockup-browser-body` element */ + bodyClass?: string | undefined + /** remaining attributes (id, data-*, aria-*, …) are forwarded to the root element */ + [key: string]: unknown +} + +import Icon from './Icon.astro' + +const { url, secure, size, class: className, bodyClass, ...rest } = Astro.props +--- + +
+
+ + { + url && ( +
+ {secure && } + {url} +
+ ) + } +
+
+