buttons: Add redesigned button styles.

This commit adds the redesigned button styles to the codebase along with
with a storybook-style page in `/devtools/buttons` to view and test the
redesigned button component.

The redesigned button component, uses the `action-button` class to
follow Zulip's no-abbreviation policy, and to avoid conflicts with the
pre-existing `button` and bootstrap `btn` classes.

A button using the new redesigned styles, required two classes,
  - First, the base `action-button` class which defines the structure
  and behavior of the button.
  - Second, a modifier class like `action-button-primary-neutral` which
  defines the styles for the particular action button type.
This commit is contained in:
Sayam Samal
2024-11-25 13:26:43 +05:30
committed by Tim Abbott
parent 7e92c2ad16
commit a8146916aa
10 changed files with 1173 additions and 0 deletions

View File

@@ -0,0 +1,179 @@
{% extends "zerver/base.html" %}
{% set entrypoint = "dev-buttons" %}
{% block title %}
<title>Button styles browser | Zulip Dev</title>
{% endblock %}
{% block content %}
<div class="portico-container" data-platform="{{ platform }}">
<div class="portico-wrap">
{% include 'zerver/portico-header.html' %}
<div class="app portico-page">
<div class="app-main portico-page-container">
<div class="design-testing-title">Button styles browser</div>
<div class="design-testing-wrapper">
<section class="action-button-section">
<div class="section-heading">Neutral Buttons</div>
<div class="action-button-group">
<button class="action-button action-button-primary-neutral" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
<button class="action-button action-button-quiet-neutral" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
<button class="action-button action-button-borderless-neutral" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
</div>
</section>
<section class="action-button-section">
<div class="section-heading">Brand Buttons</div>
<div class="action-button-group">
<button class="action-button action-button-primary-brand" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
<button class="action-button action-button-quiet-brand" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
<button class="action-button action-button-borderless-brand" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
</div>
</section>
<section class="action-button-section">
<div class="section-heading">Info Buttons</div>
<div class="action-button-group">
<button class="action-button action-button-primary-info" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
<button class="action-button action-button-quiet-info" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
<button class="action-button action-button-borderless-info" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
</div>
</section>
<section class="action-button-section">
<div class="section-heading">Success Buttons</div>
<div class="action-button-group">
<button class="action-button action-button-primary-success" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
<button class="action-button action-button-quiet-success" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
<button class="action-button action-button-borderless-success" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
</div>
</section>
<section class="action-button-section">
<div class="section-heading">Warning Buttons</div>
<div class="action-button-group">
<button class="action-button action-button-primary-warning" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
<button class="action-button action-button-quiet-warning" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
<button class="action-button action-button-borderless-warning" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
</div>
</section>
<section class="action-button-section">
<div class="section-heading">Danger Buttons</div>
<div class="action-button-group">
<button class="action-button action-button-primary-danger" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
<button class="action-button action-button-quiet-danger" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
<button class="action-button action-button-borderless-danger" tabindex=0>
<i class="zulip-icon zulip-icon-move-alt"></i>
<span class="action-button-label">Button joy</span>
</button>
</div>
</section>
<section class="action-button-section">
<div class="section-heading">Controls</div>
<div class="design-testing-controls">
<div class="design-testing-control">
<span class="control-label">Dark Theme</span>
<div role="group" class="tab-picker">
<input type="radio" id="enable_dark_theme" class="tab-option" name="dark-theme-select"/>
<label role="menuitemradio" class="tab-option-content design-testing-control-element" for="enable_dark_theme" tabindex="0">
<span>Enable</span>
</label>
<input type="radio" id="disable_dark_theme" class="tab-option" name="dark-theme-select" checked/>
<label role="menuitemradio" class="tab-option-content design-testing-control-element" for="disable_dark_theme" tabindex="0">
<span>Disable</span>
</label>
<span class="slider"></span>
</div>
</div>
<div class="design-testing-control">
<span class="control-label">Select Background</span>
<select class="design-testing-control-element" id="button_select_background">
{% for background in background_colors %}
<option value="{{ background.css_var }}" {% if background.css_var == "--color-background" %}selected{% endif %}>{{ background.name }}</option>
{% endfor %}
</select>
</div>
<div class="design-testing-control">
<span class="control-label">Button Icon</span>
<div role="group" class="tab-picker">
<input type="radio" id="enable_button_icon" class="tab-option" name="button-icon-select" checked/>
<label role="menuitemradio" class="tab-option-content design-testing-control-element" for="enable_button_icon" tabindex="0">
<span>Enable</span>
</label>
<input type="radio" id="disable_button_icon" class="tab-option" name="button-icon-select"/>
<label role="menuitemradio" class="tab-option-content design-testing-control-element" for="disable_button_icon" tabindex="0">
<span>Disable</span>
</label>
<span class="slider"></span>
</div>
</div>
<div class="design-testing-control">
<span class="control-label">Select Icon</span>
<select class="design-testing-control-element" id="button_select_icon">
{% for icon in icons %}
<option value="{{ icon }}" {% if icon == "move-alt" %}selected{% endif %}>{{ icon }}</option>
{% endfor %}
</select>
</div>
<div class="design-testing-control">
<span class="control-label">Button Text</span>
<div class="control-setting control-setting-multiple">
<input class="design-testing-control-element" type="text" id="button_text" />
<button class="design-testing-control-element" id="clear_button_text">Reset</button>
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -79,6 +79,11 @@
<td>None needed</td>
<td>Test incoming webhook integrations</td>
</tr>
<tr>
<td><a href="/devtools/buttons">/devtools/buttons</a></td>
<td>None needed</td>
<td>Test button styles</td>
</tr>
</tbody>
</table>
<h2>Useful management commands</h2>

View File

@@ -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})`);
});
});

View File

@@ -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 {

356
web/styles/buttons.css Normal file
View File

@@ -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
);
}
}

View File

@@ -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;
}

View File

@@ -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"
]
}

View File

@@ -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.

View File

@@ -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)

View File

@@ -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/<digest>/<received_url>", handle_camo_url),
# Endpoints for design testing.
path("devtools/buttons/", dev_buttons_design_testing),
]
v1_api_mobile_patterns = [