mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 04:52:12 +00:00
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:
179
templates/zerver/development/design_testing/buttons.html
Normal file
179
templates/zerver/development/design_testing/buttons.html
Normal 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 %}
|
@@ -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>
|
||||
|
43
web/src/portico/design-testing.ts
Normal file
43
web/src/portico/design-testing.ts
Normal 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})`);
|
||||
});
|
||||
});
|
@@ -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
356
web/styles/buttons.css
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
113
web/styles/portico/dev-buttons.css
Normal file
113
web/styles/portico/dev-buttons.css
Normal 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;
|
||||
}
|
@@ -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"
|
||||
]
|
||||
}
|
||||
|
@@ -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.
|
||||
|
34
zerver/views/development/design_testing.py
Normal file
34
zerver/views/development/design_testing.py
Normal 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)
|
@@ -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 = [
|
||||
|
Reference in New Issue
Block a user