diff --git a/templates/zerver/development/design_testing/buttons.html b/templates/zerver/development/design_testing/buttons.html new file mode 100644 index 0000000000..59b5317221 --- /dev/null +++ b/templates/zerver/development/design_testing/buttons.html @@ -0,0 +1,179 @@ +{% extends "zerver/base.html" %} +{% set entrypoint = "dev-buttons" %} + +{% block title %} +Button styles browser | Zulip Dev +{% endblock %} + +{% block content %} +
+
+ {% include 'zerver/portico-header.html' %} +
+
+
Button styles browser
+
+
+
Neutral Buttons
+
+ + + +
+
+
+
Brand Buttons
+
+ + + +
+
+
+
Info Buttons
+
+ + + +
+
+
+
Success Buttons
+
+ + + +
+
+
+
Warning Buttons
+
+ + + +
+
+
+
Danger Buttons
+
+ + + +
+
+
+
Controls
+
+
+ Dark Theme +
+ + + + + +
+
+
+ Select Background + +
+
+ Button Icon +
+ + + + + +
+
+
+ Select Icon + +
+
+ Button Text +
+ + +
+
+
+
+
+
+
+
+
+{% endblock %} diff --git a/templates/zerver/development/dev_tools.html b/templates/zerver/development/dev_tools.html index 84381701a5..b803ce4a0e 100644 --- a/templates/zerver/development/dev_tools.html +++ b/templates/zerver/development/dev_tools.html @@ -79,6 +79,11 @@ None needed Test incoming webhook integrations + + /devtools/buttons + None needed + Test button styles +

Useful management commands

diff --git a/web/src/portico/design-testing.ts b/web/src/portico/design-testing.ts new file mode 100644 index 0000000000..62bf91d35f --- /dev/null +++ b/web/src/portico/design-testing.ts @@ -0,0 +1,43 @@ +import $ from "jquery"; + +import {$t} from "../i18n.ts"; + +$(window).on("load", () => { + $("input[name='dark-theme-select']").on("change", (e) => { + if ($(e.target).attr("id") === "enable_dark_theme") { + $(":root").addClass("dark-theme"); + } else { + $(":root").removeClass("dark-theme"); + } + }); + + $("input[name='button-icon-select']").on("change", (e) => { + if ($(e.target).attr("id") === "enable_button_icon") { + $(".action-button .zulip-icon").removeClass("hidden"); + } else { + $(".action-button .zulip-icon").addClass("hidden"); + } + }); + + $("#button_text").on("input", function (this: HTMLElement) { + const button_text = $(this).val()?.toString() ?? ""; + $(".action-button-label").text(button_text); + }); + + $("#clear_button_text").on("click", () => { + $("#button_text").val(""); + $(".action-button-label").text($t({defaultMessage: "Button joy"})); + }); + + $("#button_select_icon").on("change", function (this: HTMLElement) { + const icon_name = $(this).val()?.toString() ?? ""; + $(".action-button .zulip-icon").attr("class", (_index, className) => + className.replaceAll(/zulip-icon-[^\s]+/g, `zulip-icon-${icon_name}`), + ); + }); + + $("#button_select_background").on("change", function (this: HTMLElement) { + const background_var = $(this).val()?.toString() ?? ""; + $("body").css("background-color", `var(${background_var})`); + }); +}); diff --git a/web/styles/app_variables.css b/web/styles/app_variables.css index 99f86369aa..61289d2dde 100644 --- a/web/styles/app_variables.css +++ b/web/styles/app_variables.css @@ -1106,6 +1106,209 @@ --background-color-active-dropdown-item: hsl(220deg 12% 4.9% / 5%); --background-color-active-typeahead-item: hsl(221.14deg 89.74% 92.35%); --color-typeahead-option-label: var(--grey-500); + + /* Actions buttons */ + --color-inner-shadow-action-button: color-mix( + in oklch, + #000 10%, + transparent + ); + /* Action buttons -- Neutral Variant */ + --color-text-neutral-primary-action-button: #fff; + --color-background-neutral-primary-action-button: #777a88; + --color-background-neutral-primary-action-button-hover: #707380; + --color-background-neutral-primary-action-button-active: #696b78; + --color-text-neutral-quiet-action-button: #393c49; + --color-background-neutral-quiet-action-button: color-mix( + in oklch, + #767988 12%, + transparent + ); + --color-background-neutral-quiet-action-button-hover: color-mix( + in oklch, + #767988 17%, + transparent + ); + --color-background-neutral-quiet-action-button-active: color-mix( + in oklch, + #767988 22%, + transparent + ); + --color-text-neutral-borderless-action-button: #535663; + --color-background-neutral-borderless-action-button-hover: color-mix( + in oklch, + #535663 7%, + transparent + ); + --color-background-neutral-borderless-action-button-active: color-mix( + in oklch, + #535663 11%, + transparent + ); + /* Action buttons -- Brand Variant */ + --color-text-brand-primary-action-button: #fff; + --color-background-brand-primary-action-button: #805bfe; + --color-background-brand-primary-action-button-hover: #7349ec; + --color-background-brand-primary-action-button-active: #612fd4; + --color-text-brand-quiet-action-button: #4704aa; + --color-background-brand-quiet-action-button: color-mix( + in oklch, + #805bfe 12%, + transparent + ); + --color-background-brand-quiet-action-button-hover: color-mix( + in oklch, + #805bfe 17%, + transparent + ); + --color-background-brand-quiet-action-button-active: color-mix( + in oklch, + #805bfe 22%, + transparent + ); + --color-text-brand-borderless-action-button: #5f3cc5; + --color-background-brand-borderless-action-button-hover: color-mix( + in oklch, + #2c0070 5%, + transparent + ); + --color-background-brand-borderless-action-button-active: color-mix( + in oklch, + #2c0070 10%, + transparent + ); + /* Action buttons -- Info Variant */ + --color-text-info-primary-action-button: #fff; + --color-background-info-primary-action-button: #3c6bff; + --color-background-info-primary-action-button-hover: #2e59eb; + --color-background-info-primary-action-button-active: #1e41d3; + --color-text-info-quiet-action-button: #1027a6; + --color-background-info-quiet-action-button: color-mix( + in oklch, + #3c6bff 12%, + transparent + ); + --color-background-info-quiet-action-button-hover: color-mix( + in oklch, + #3c6bff 17%, + transparent + ); + --color-background-info-quiet-action-button-active: color-mix( + in oklch, + #3c6bff 22%, + transparent + ); + --color-text-info-borderless-action-button: #2347c6; + --color-background-info-borderless-action-button-hover: color-mix( + in oklch, + #06037c 5%, + transparent + ); + --color-background-info-borderless-action-button-active: color-mix( + in oklch, + #06037c 9%, + transparent + ); + /* Action buttons -- Success Variant */ + --color-text-success-primary-action-button: #fff; + --color-background-success-primary-action-button: #07833c; + --color-background-success-primary-action-button-hover: #087736; + --color-background-success-primary-action-button-active: #09672e; + --color-text-success-quiet-action-button: #054f22; + --color-background-success-quiet-action-button: color-mix( + in oklch, + #07833c 13%, + transparent + ); + --color-background-success-quiet-action-button-hover: color-mix( + in oklch, + #07833c 18%, + transparent + ); + --color-background-success-quiet-action-button-active: color-mix( + in oklch, + #07833c 23%, + transparent + ); + --color-text-success-borderless-action-button: #07833c; + --color-background-success-borderless-action-button-hover: color-mix( + in oklch, + #09672e 8%, + transparent + ); + --color-background-success-borderless-action-button-active: color-mix( + in oklch, + #09672e 12%, + transparent + ); + /* Action buttons -- Warning Variant */ + --color-text-warning-primary-action-button: color-mix( + in oklch, + #000 88%, + transparent + ); + --color-background-warning-primary-action-button: #febe3d; + --color-background-warning-primary-action-button-hover: #f8b325; + --color-background-warning-primary-action-button-active: #eba002; + --color-text-warning-quiet-action-button: #764607; + --color-background-warning-quiet-action-button: color-mix( + in oklch, + #eba002 18%, + transparent + ); + --color-background-warning-quiet-action-button-hover: color-mix( + in oklch, + #eba002 23%, + transparent + ); + --color-background-warning-quiet-action-button-active: color-mix( + in oklch, + #eba002 28%, + transparent + ); + --color-text-warning-borderless-action-button: #a96a05; + --color-background-warning-borderless-action-button-hover: color-mix( + in oklch, + #a96a05 10%, + transparent + ); + --color-background-warning-borderless-action-button-active: color-mix( + in oklch, + #a96a05 14%, + transparent + ); + /* Action buttons -- Danger Variant */ + --color-text-danger-primary-action-button: #fff; + --color-background-danger-primary-action-button: #e1392e; + --color-background-danger-primary-action-button-hover: #d22720; + --color-background-danger-primary-action-button-active: #c0070a; + --color-text-danger-quiet-action-button: #ac0508; + --color-background-danger-quiet-action-button: color-mix( + in oklch, + #e1392e 13%, + transparent + ); + --color-background-danger-quiet-action-button-hover: color-mix( + in oklch, + #e1392e 18%, + transparent + ); + --color-background-danger-quiet-action-button-active: color-mix( + in oklch, + #e1392e 23%, + transparent + ); + --color-text-danger-borderless-action-button: #c0070a; + --color-background-danger-borderless-action-button-hover: color-mix( + in oklch, + #c0070a 9%, + transparent + ); + --color-background-danger-borderless-action-button-active: color-mix( + in oklch, + #c0070a 13%, + transparent + ); } %dark-theme { @@ -1641,6 +1844,223 @@ 226.35deg 82.53% 55.1% / 38.82% ); --color-typeahead-option-label: var(--grey-400); + + /* Action buttons -- Neutral Variant */ + --color-text-neutral-primary-action-button: color-mix( + in oklch, + #fff 85%, + transparent + ); + --color-background-neutral-primary-action-button: #535663; + --color-background-neutral-primary-action-button-hover: #626573; + --color-background-neutral-primary-action-button-active: #535663; + --color-text-neutral-quiet-action-button: #bbbdc8; + --color-background-neutral-quiet-action-button: color-mix( + in oklch, + #bbbdc8 12%, + transparent + ); + --color-background-neutral-quiet-action-button-hover: color-mix( + in oklch, + #bbbdc8 17%, + transparent + ); + --color-background-neutral-quiet-action-button-active: color-mix( + in oklch, + #bbbdc8 12%, + transparent + ); + --color-text-neutral-borderless-action-button: #aaadba; + --color-background-neutral-borderless-action-button-hover: color-mix( + in oklch, + #9194a3 14%, + transparent + ); + --color-background-neutral-borderless-action-button-active: color-mix( + in oklch, + #9194a3 18%, + transparent + ); + /* Action buttons -- Brand Variant */ + --color-text-brand-primary-action-button: color-mix( + in oklch, + #fff 85%, + transparent + ); + --color-background-brand-primary-action-button: #5417c3; + --color-background-brand-primary-action-button-hover: #612fd4; + --color-background-brand-primary-action-button-active: #5417c3; + --color-text-brand-quiet-action-button: #aba5fd; + --color-background-brand-quiet-action-button: color-mix( + in oklch, + #9e94fd 12%, + transparent + ); + --color-background-brand-quiet-action-button-hover: color-mix( + in oklch, + #9e94fd 17%, + transparent + ); + --color-background-brand-quiet-action-button-active: color-mix( + in oklch, + #9e94fd 12%, + transparent + ); + --color-text-brand-borderless-action-button: #9e94fd; + --color-background-brand-borderless-action-button-hover: color-mix( + in oklch, + #8a6fff 14%, + transparent + ); + --color-background-brand-borderless-action-button-active: color-mix( + in oklch, + #8a6fff 18%, + transparent + ); + /* Action buttons -- Info Variant */ + --color-text-info-primary-action-button: color-mix( + in oklch, + #fff 85%, + transparent + ); + --color-background-info-primary-action-button: #1e41d3; + --color-background-info-primary-action-button-hover: #2e59eb; + --color-background-info-primary-action-button-active: #1e41d3; + --color-text-info-quiet-action-button: #97b6fe; + --color-background-info-quiet-action-button: color-mix( + in oklch, + #97b6fe 12%, + transparent + ); + --color-background-info-quiet-action-button-hover: color-mix( + in oklch, + #97b6fe 17%, + transparent + ); + --color-background-info-quiet-action-button-active: color-mix( + in oklch, + #97b6fe 12%, + transparent + ); + --color-text-info-borderless-action-button: #84a8fd; + --color-background-info-borderless-action-button-hover: color-mix( + in oklch, + #4d7bfd 12%, + transparent + ); + --color-background-info-borderless-action-button-active: color-mix( + in oklch, + #4d7bfd 17%, + transparent + ); + /* Action buttons -- Success Variant */ + --color-text-success-primary-action-button: color-mix( + in oklch, + #fff 85%, + transparent + ); + --color-background-success-primary-action-button: #09672e; + --color-background-success-primary-action-button-hover: #087736; + --color-background-success-primary-action-button-active: #09672e; + --color-text-success-quiet-action-button: #6bd586; + --color-background-success-quiet-action-button: color-mix( + in oklch, + #41ae61 12%, + transparent + ); + --color-background-success-quiet-action-button-hover: color-mix( + in oklch, + #41ae61 17%, + transparent + ); + --color-background-success-quiet-action-button-active: color-mix( + in oklch, + #41ae61 12%, + transparent + ); + --color-text-success-borderless-action-button: #57c273; + --color-background-success-borderless-action-button-hover: color-mix( + in oklch, + #2f9f52 12%, + transparent + ); + --color-background-success-borderless-action-button-active: color-mix( + in oklch, + #2f9f52 17%, + transparent + ); + /* Action buttons -- Warning Variant */ + --color-text-warning-primary-action-button: color-mix( + in oklch, + #000 90%, + transparent + ); + --color-background-warning-primary-action-button: #db920d; + --color-background-warning-primary-action-button-hover: #eba002; + --color-background-warning-primary-action-button-active: #db920d; + --color-text-warning-quiet-action-button: #f8b325; + --color-background-warning-quiet-action-button: color-mix( + in oklch, + #db920d 12%, + transparent + ); + --color-background-warning-quiet-action-button-hover: color-mix( + in oklch, + #db920d 17%, + transparent + ); + --color-background-warning-quiet-action-button-active: color-mix( + in oklch, + #db920d 12%, + transparent + ); + --color-text-warning-borderless-action-button: #eba002; + --color-background-warning-borderless-action-button-hover: color-mix( + in oklch, + #c8850d 12%, + transparent + ); + --color-background-warning-borderless-action-button-active: color-mix( + in oklch, + #c8850d 17%, + transparent + ); + /* Action buttons -- Danger Variant */ + --color-text-danger-primary-action-button: color-mix( + in oklch, + #fff 85%, + transparent + ); + --color-background-danger-primary-action-button: #d22720; + --color-background-danger-primary-action-button-hover: #e1392e; + --color-background-danger-primary-action-button-active: #d22720; + --color-text-danger-quiet-action-button: #ff8b7c; + --color-background-danger-quiet-action-button: color-mix( + in oklch, + #fd5f50 12%, + transparent + ); + --color-background-danger-quiet-action-button-hover: color-mix( + in oklch, + #fd5f50 17%, + transparent + ); + --color-background-danger-quiet-action-button-active: color-mix( + in oklch, + #fd5f50 12%, + transparent + ); + --color-text-danger-borderless-action-button: #ff8b7c; + --color-background-danger-borderless-action-button-hover: color-mix( + in oklch, + #f34c3e 12%, + transparent + ); + --color-background-danger-borderless-action-button-active: color-mix( + in oklch, + #f34c3e 17%, + transparent + ); } @media screen { diff --git a/web/styles/buttons.css b/web/styles/buttons.css new file mode 100644 index 0000000000..dcbdfa962a --- /dev/null +++ b/web/styles/buttons.css @@ -0,0 +1,356 @@ +.action-button { + display: flex; + gap: 0.5ch; + justify-content: center; + align-items: center; + line-height: 1.3333; + font-size: var(--base-font-size-px); + font-family: "Source Sans 3 VF", sans-serif; + font-weight: 550; + letter-spacing: 0.02ch; + padding: 0.2667em 0.6667em; + color: var(--color-text-neutral-quiet-action-button); + background-color: var(--color-background-neutral-quiet-action-button); + border-radius: 4px; + white-space: nowrap; + user-select: none; + border: none; + cursor: pointer; + + &:hover { + background-color: var( + --color-background-neutral-quiet-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-neutral-quiet-action-button-active + ); + scale: 0.96; + } +} + +.action-button-label { + max-width: 32ch; + text-overflow: ellipsis; + overflow: hidden; +} + +/* Action buttons -- Neutral Intent */ +.action-button-primary-neutral { + color: var(--color-text-neutral-primary-action-button); + background-color: var(--color-background-neutral-primary-action-button); + + &:hover { + background-color: var( + --color-background-neutral-primary-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-neutral-primary-action-button-active + ); + } +} + +.action-button-quiet-neutral { + color: var(--color-text-neutral-quiet-action-button); + background-color: var(--color-background-neutral-quiet-action-button); + box-shadow: 0 0 0.5px 0.5px var(--color-inner-shadow-action-button) inset; + + &:hover { + background-color: var( + --color-background-neutral-quiet-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-neutral-quiet-action-button-active + ); + } +} + +.action-button-borderless-neutral { + color: var(--color-text-neutral-borderless-action-button); + background-color: transparent; + + &:hover { + background-color: var( + --color-background-neutral-borderless-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-neutral-borderless-action-button-active + ); + } +} + +/* Action buttons -- Brand Intent */ +.action-button-primary-brand { + color: var(--color-text-brand-primary-action-button); + background-color: var(--color-background-brand-primary-action-button); + + &:hover { + background-color: var( + --color-background-brand-primary-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-brand-primary-action-button-active + ); + } +} + +.action-button-quiet-brand { + color: var(--color-text-brand-quiet-action-button); + background-color: var(--color-background-brand-quiet-action-button); + box-shadow: 0 0 0.5px 0.5px var(--color-inner-shadow-action-button) inset; + + &:hover { + background-color: var( + --color-background-brand-quiet-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-brand-quiet-action-button-active + ); + } +} + +.action-button-borderless-brand { + color: var(--color-text-brand-borderless-action-button); + background-color: transparent; + + &:hover { + background-color: var( + --color-background-brand-borderless-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-brand-borderless-action-button-active + ); + } +} + +/* Action buttons -- Info Intent */ +.action-button-primary-info { + color: var(--color-text-info-primary-action-button); + background-color: var(--color-background-info-primary-action-button); + + &:hover { + background-color: var( + --color-background-info-primary-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-info-primary-action-button-active + ); + } +} + +.action-button-quiet-info { + color: var(--color-text-info-quiet-action-button); + background-color: var(--color-background-info-quiet-action-button); + box-shadow: 0 0 0.5px 0.5px var(--color-inner-shadow-action-button) inset; + + &:hover { + background-color: var( + --color-background-info-quiet-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-info-quiet-action-button-active + ); + } +} + +.action-button-borderless-info { + color: var(--color-text-info-borderless-action-button); + background-color: transparent; + + &:hover { + background-color: var( + --color-background-info-borderless-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-info-borderless-action-button-active + ); + } +} + +/* Action buttons -- Success Intent */ +.action-button-primary-success { + color: var(--color-text-success-primary-action-button); + background-color: var(--color-background-success-primary-action-button); + + &:hover { + background-color: var( + --color-background-success-primary-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-success-primary-action-button-active + ); + } +} + +.action-button-quiet-success { + color: var(--color-text-success-quiet-action-button); + background-color: var(--color-background-success-quiet-action-button); + box-shadow: 0 0 0.5px 0.5px var(--color-inner-shadow-action-button) inset; + + &:hover { + background-color: var( + --color-background-success-quiet-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-success-quiet-action-button-active + ); + } +} + +.action-button-borderless-success { + color: var(--color-text-success-borderless-action-button); + background-color: transparent; + + &:hover { + background-color: var( + --color-background-success-borderless-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-success-borderless-action-button-active + ); + } +} + +/* Action buttons -- Warning Intent */ +.action-button-primary-warning { + color: var(--color-text-warning-primary-action-button); + background-color: var(--color-background-warning-primary-action-button); + + &:hover { + background-color: var( + --color-background-warning-primary-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-warning-primary-action-button-active + ); + } +} + +.action-button-quiet-warning { + color: var(--color-text-warning-quiet-action-button); + background-color: var(--color-background-warning-quiet-action-button); + box-shadow: 0 0 0.5px 0.5px var(--color-inner-shadow-action-button) inset; + + &:hover { + background-color: var( + --color-background-warning-quiet-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-warning-quiet-action-button-active + ); + } +} + +.action-button-borderless-warning { + color: var(--color-text-warning-borderless-action-button); + background-color: transparent; + + &:hover { + background-color: var( + --color-background-warning-borderless-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-warning-borderless-action-button-active + ); + } +} + +/* Action buttons -- Danger Intent */ +.action-button-primary-danger { + color: var(--color-text-danger-primary-action-button); + background-color: var(--color-background-danger-primary-action-button); + + &:hover { + background-color: var( + --color-background-danger-primary-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-danger-primary-action-button-active + ); + } +} + +.action-button-quiet-danger { + color: var(--color-text-danger-quiet-action-button); + background-color: var(--color-background-danger-quiet-action-button); + box-shadow: 0 0 0.5px 0.5px var(--color-inner-shadow-action-button) inset; + + &:hover { + background-color: var( + --color-background-danger-quiet-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-danger-quiet-action-button-active + ); + } +} + +.action-button-borderless-danger { + color: var(--color-text-danger-borderless-action-button); + background-color: transparent; + + &:hover { + background-color: var( + --color-background-danger-borderless-action-button-hover + ); + } + + &:active { + background-color: var( + --color-background-danger-borderless-action-button-active + ); + } +} diff --git a/web/styles/portico/dev-buttons.css b/web/styles/portico/dev-buttons.css new file mode 100644 index 0000000000..9c95dc2b8b --- /dev/null +++ b/web/styles/portico/dev-buttons.css @@ -0,0 +1,113 @@ +body { + --base-font-size-px: 16px; + width: 100%; + margin: 0; + padding: 0; + font-size: var(--base-font-size-px); + /* The line-height used in most of the UI should be at least + its legacy value, but should expand with larger base + line-height values. */ + line-height: max( + var(--legacy-body-line-height-unitless), + var(--base-line-height-unitless) + ); + font-family: "Source Sans 3 VF", sans-serif; + font-weight: unset; /* override value in portico.css */ + color: var(--color-text-default); + background-color: var(--color-background); +} + +.design-testing-header { + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + padding: 15px; + background-color: hsl(0deg 0% 100%); + box-shadow: 0 0 4px hsla(0deg 0% 0% / 10%); + + & .header__title { + color: hsl(0deg 0% 27%); + text-align: center; + font-size: 1.4em; + font-weight: 300; + } + + & .header__link { + text-decoration: none; + color: hsl(0deg 0% 27%); + font-weight: 600; + } +} + +.design-testing-wrapper { + display: flex; + flex-flow: column; + gap: 20px; + padding-bottom: 20px; +} + +.design-testing-title { + font-size: 2em; + text-align: center; + padding: 20px 0; +} + +.action-button-section { + display: flex; + flex-flow: column; + gap: 5px; +} + +.design-testing-controls { + display: grid; + grid-template-columns: [control-name-start] max-content [control-name-end control-input-start] min-content [control-input-end]; + grid-auto-rows: 1fr; + gap: 10px; + background-color: var(--color-background); + border: solid 1px; + padding: 10px; + width: fit-content; +} + +.design-testing-control { + display: grid; + grid-template-columns: subgrid; + grid-column: control-name-start / control-input-end; + gap: 10px; +} + +.design-testing-control-element { + font-size: inherit; + font-family: inherit; +} + +.control-label { + cursor: pointer; + grid-column: control-name-start / control-name-end; + align-self: center; +} + +.control-setting { + grid-column: control-input-start / control-input-end; +} + +.control-setting-multiple { + display: flex; + align-items: center; + gap: 10px; +} + +.hidden { + display: none !important; +} + +.section-heading { + font-size: 1.4em; + font-weight: 600; +} + +.action-button-group { + display: flex; + flex-flow: row wrap; + gap: 10px; +} diff --git a/web/webpack.dev-assets.json b/web/webpack.dev-assets.json index d50b045065..b9ef8d7db7 100644 --- a/web/webpack.dev-assets.json +++ b/web/webpack.dev-assets.json @@ -14,5 +14,15 @@ "./styles/portico/email_log.css", "./src/reload_state.ts", "./src/channel.ts" + ], + "dev-buttons": [ + "./src/bundles/common.ts", + "./src//portico/header.ts", + "./src/portico/design-testing.ts", + "./styles/portico/portico_styles.css", + "./styles/portico/dev-buttons.css", + "./styles/app_variables.css", + "./styles/app_components.css", + "./styles/buttons.css" ] } diff --git a/zerver/tests/test_urls.py b/zerver/tests/test_urls.py index 08bb51ac8f..9e981d1670 100644 --- a/zerver/tests/test_urls.py +++ b/zerver/tests/test_urls.py @@ -56,6 +56,16 @@ class PublicURLTest(ZulipTestCase): self.assertIn(expected_tag, response.content.decode()) self.assertEqual(response.status_code, 200) + def test_design_testing_pages(self) -> None: + urls = { + "/devtools/buttons/": "Button styles browser", + } + + for url, expected_content in urls.items(): + result = self.client_get(url) + self.assertEqual(result.status_code, 200) + self.assert_in_success_response([expected_content], result) + def test_public_urls(self) -> None: """ Test which views are accessible when not logged in. diff --git a/zerver/views/development/design_testing.py b/zerver/views/development/design_testing.py new file mode 100644 index 0000000000..3d9e0755f8 --- /dev/null +++ b/zerver/views/development/design_testing.py @@ -0,0 +1,34 @@ +import os + +from django.http import HttpRequest, HttpResponse +from django.shortcuts import render + +background_colors = [ + {"name": "Default background", "css_var": "--color-background"}, + {"name": "Popover background", "css_var": "--color-background-popover-menu"}, + {"name": "Modal background", "css_var": "--color-background-modal"}, + {"name": "Compose background", "css_var": "--color-compose-box-background"}, +] + + +def get_svg_filenames() -> list[str]: + icons_dir = os.path.join(os.path.dirname(__file__), "../../../web/shared/icons") + + # Get all .svg file names from the directory + svg_files = [f for f in os.listdir(icons_dir) if f.endswith(".svg")] + + # Remove the .svg extension from the file names + icon_names = [os.path.splitext(f)[0] for f in svg_files] + + # Sort the list alphabetically + return sorted(icon_names) + + +def dev_buttons_design_testing(request: HttpRequest) -> HttpResponse: + context = { + "background_colors": background_colors, + "icons": get_svg_filenames(), + # We set isolated_page to avoid clutter from footer/header. + "isolated_page": True, + } + return render(request, "zerver/development/design_testing/buttons.html", context) diff --git a/zproject/dev_urls.py b/zproject/dev_urls.py index 53ac877d0b..d8164bfb1d 100644 --- a/zproject/dev_urls.py +++ b/zproject/dev_urls.py @@ -13,6 +13,7 @@ from django.views.static import serve from zerver.views.auth import login_page from zerver.views.development.cache import remove_caches from zerver.views.development.camo import handle_camo_url +from zerver.views.development.design_testing import dev_buttons_design_testing from zerver.views.development.dev_login import ( api_dev_fetch_api_key, api_dev_list_users, @@ -97,6 +98,8 @@ urls = [ path("flush_caches", remove_caches), # Redirect camo URLs for development path("external_content//", handle_camo_url), + # Endpoints for design testing. + path("devtools/buttons/", dev_buttons_design_testing), ] v1_api_mobile_patterns = [