svelte: Reduce imported global CSS (#62457)

Until now we've simply imported the global `base.scss` file, which in
turn imports a bunch of other files and defines global CSS classes. Many
(most?) of them are actually not used.

This commit reduces to the number of imported files to the ones that
contain styles that are used. My methodology was to inspect every
file that `base.scss` imports and find out whether it defines custom
properties (variables) or global classes. In some cases I extracted
variables into their own files.
This commit is contained in:
Felix Kling 2024-05-08 15:51:31 +02:00 committed by GitHub
parent ee376b66e7
commit c1410f0f3c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 245 additions and 76 deletions

View File

@ -45,7 +45,7 @@
</div>
{/if}
<div class="info">
<span class="d-flex">
<span class="title">
<a class="subject" href={commit.canonicalURL}>{commit.subject}</a>
{#if !alwaysExpanded && commit.body}
<button type="button" on:click={() => (expanded = !expanded)}>
@ -73,17 +73,21 @@
min-width: 0;
}
.subject {
font-weight: 600;
flex: 0 1 auto;
padding-right: 0.5rem;
color: var(--body-color);
min-width: 0;
.title {
display: flex;
gap: 0.5rem;
@media (--sm-breakpoint-up) {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
.subject {
font-weight: 600;
flex: 0 1 auto;
color: var(--body-color);
min-width: 0;
@media (--sm-breakpoint-up) {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}

View File

@ -10,10 +10,9 @@
<script lang="ts">
import type { SVGAttributes } from 'svelte/elements'
interface $$Props extends SVGAttributes<SVGElement> {
type $$Props = SVGAttributes<SVGElement> & {
svgPath: string
inline?: boolean
size?: number
}
export let svgPath: string
@ -34,6 +33,7 @@
&.icon-inline {
width: $iconInlineSize;
height: $iconInlineSize;
vertical-align: text-bottom;
}
color: var(--icon-fill-color, var(--color, inherit));
fill: currentColor;

View File

@ -35,13 +35,11 @@
</script>
<li
class="treeitem"
class:selectable
class:selected
role="treeitem"
aria-selected={selectable ? selected : undefined}
aria-expanded={expandable ? expanded : undefined}
{tabindex}
data-treeitem
data-node-id={nodeID}
>
<span class="label" data-treeitem-label>
@ -65,11 +63,11 @@
</span>
{#if expanded && children}
{#await children}
<div class="ml-4">
<div class="loading">
<LoadingSpinner center={false} />
</div>
{:then treeProvider}
<ul role="group" class="ml-2">
<ul role="group">
{#each treeProvider.getEntries() as entry (treeProvider.getNodeID(entry))}
<svelte:self {entry} {treeProvider} let:entry let:toggle let:expanded>
<slot {entry} {toggle} {expanded} />
@ -83,7 +81,7 @@
</li>
<style lang="scss">
li {
[role='treeitem'] {
// Margin ensures that focus rings are not covered by preceeding or following elements
margin: 0.25rem 0;
border-radius: var(--border-radius);
@ -97,6 +95,11 @@
}
}
[role='group'],
.loading {
margin-left: 1rem;
}
.label {
display: flex;
align-items: center;

View File

@ -1,5 +1,6 @@
<script lang="ts" context="module">
import { Story, Template } from '@storybook/addon-svelte-csf'
import {
createEmptySingleSelectTreeState,
updateTreeState,
@ -95,10 +96,10 @@
<style lang="scss">
:global(.label:hover),
:global(.treeitem.selected) > :global(.label) {
:global([data-treeitem][aria-selected]) > :global(.label) {
background-color: lightblue;
}
:global(.treeitem:focus) > :global(.label) {
:global([data-treeitem]:focus) > :global(.label) {
outline: 2px solid green !important;
}
</style>

View File

@ -1 +0,0 @@
@import 'wildcard/src/global-styles/breakpoints.scss';

View File

@ -35,8 +35,8 @@
{#each squares as type}
<span
class="square"
class:bg-success={type === SquareType.Added}
class:bg-danger={type === SquareType.Deleted}
class:added={type === SquareType.Added}
class:deleted={type === SquareType.Deleted}
/>
{/each}
</span>
@ -54,4 +54,12 @@
height: 0.5rem;
background-color: var(--color-bg-3);
}
.added {
background-color: var(--success);
}
.deleted {
background-color: var(--danger);
}
</style>

View File

@ -8,7 +8,7 @@
$: decorations = decorateQuery(query)
</script>
<span class="text-monospace search-query-link">
<code class="search-query-link">
{#if decorations}
{#each decorations as { key, className, value, token } (key)}
<span class={className}>
@ -20,4 +20,4 @@
{:else}
{query}
{/if}
</span>
</code>

View File

@ -39,12 +39,12 @@
<li role="row" id="{groupIndex}x{rowIndex}" aria-selected={selected}>
{#if option.icon}
<div class="pr-1 align-self-start">
<div class="icon">
<Icon svgPath={option.icon} aria-hidden="true" inline />
</div>
{/if}
<div class="inner-row">
<div class="d-flex flex-wrap">
<div class="content">
<div role="gridcell" class="label test-option-label">
{#if field}
<span class="filter-option">
@ -98,6 +98,7 @@
font-family: var(--code-font-family);
font-size: 0.75rem;
min-height: 1.5rem;
gap: 0.25rem;
&[aria-selected='true'] {
background-color: var(--subtle-bg);
@ -109,6 +110,10 @@
cursor: pointer;
}
.icon {
align-self: start;
}
// Used to make label and actions wrappable
.inner-row {
display: flex;
@ -122,6 +127,11 @@
}
}
.content {
display: flex;
flex-wrap: wrap;
}
.label {
margin-right: 0.5rem;
}

View File

@ -0,0 +1,22 @@
$viewport-sm: 576px;
$viewport-md: 768px;
$viewport-lg: 992px;
$viewport-xl: 1200px;
/* stylelint-disable unknownAtRules */
@custom-media --xs-breakpoint-up (min-width: none);
@custom-media --sm-breakpoint-up (min-width: #{$viewport-sm});
@custom-media --md-breakpoint-up (min-width: #{$viewport-md});
@custom-media --lg-breakpoint-up (min-width: #{$viewport-lg});
@custom-media --xl-breakpoint-up (min-width: #{$viewport-xl});
// The maximum value is calculated as the minimum of the next one less 0.02px
// to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths.
// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max
// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.
// See https://bugs.webkit.org/show_bug.cgi?id=178261
@custom-media --xs-breakpoint-down (max-width: #{$viewport-sm - .02});
@custom-media --sm-breakpoint-down (max-width: #{$viewport-md - .02});
@custom-media --md-breakpoint-down (max-width: #{$viewport-lg - .02});
@custom-media --lg-breakpoint-down (max-width: #{$viewport-xl - .02});
@custom-media --xl-breakpoint-down (max-width: none);

View File

@ -0,0 +1,43 @@
:root {
--dropdown-inner-border-radius: 0.1875rem;
--dropdown-padding-y: 0.5rem;
--dropdown-item-padding-y: 0.25rem;
--dropdown-item-padding-x: 0.5rem;
--dropdown-item-padding: var(--dropdown-item-padding-y) var(--dropdown-item-padding-x);
--dropdown-min-width: 10rem;
--dropdown-spacer: 0.125rem;
--dropdown-border-width: 0.06rem;
--dropdown-header-font-size: 0.765625rem;
--dropdown-header-padding: var(--dropdown-padding-y) var(--dropdown-item-padding-x);
--caret-width: 0.3em;
--caret-spacing: calc(var(--caret-spacing) * 0.85);
--caret-vertical-align: var(--caret-spacing);
}
.theme-light {
--dropdown-header-color: var(--text-muted);
--dropdown-border-color: var(--input-border-color);
--dropdown-bg: var(--color-bg-1);
--dropdown-color: var(--body-color);
--dropdown-link-color: var(--body-color);
--dropdown-link-hover-bg: var(--color-bg-2);
--dropdown-link-hover-color: var(--body-color);
--dropdown-link-active-bg: var(--primary);
--dropdown-link-active-color: var(--white);
--dropdown-link-disabled-color: var(--text-muted);
--dropdown-shadow: 0 4px 16px -6px rgba(36, 41, 54, 0.2);
}
.theme-dark {
--dropdown-header-color: var(--text-muted);
--dropdown-border-color: var(--input-border-color);
--dropdown-bg: var(--color-bg-1);
--dropdown-color: var(--body-color);
--dropdown-link-color: var(--body-color);
--dropdown-link-hover-bg: var(--color-bg-3);
--dropdown-link-hover-color: var(--body-color);
--dropdown-link-active-bg: var(--primary);
--dropdown-link-active-color: var(--white);
--dropdown-link-disabled-color: var(--text-muted);
--dropdown-shadow: 0 4px 16px -6px rgba(11, 12, 15, 0.8);
}

View File

@ -0,0 +1,9 @@
:root {
--popover-bg: var(--white);
--popover-border-width: var(--border-width);
--popover-border-color: rgba(var(--black), 0.2);
--popover-box-shadow: 0 0.25rem 0.5rem rgba(var(--black), 0.2);
--zindex-popover: 1060;
--popover-font-size: var(--font-size-base);
--popover-max-width: auto;
}

View File

@ -0,0 +1,5 @@
:root {
// border-radius
--border-radius: 3px;
--popover-border-radius: 5px;
}

View File

@ -153,7 +153,7 @@
div {
overflow: auto;
:global(.treeitem.selectable) > :global(.label) {
:global([data-treeitem][aria-selected]) > :global([data-treeitem-label]) {
cursor: pointer;
border-radius: var(--border-radius);
@ -162,7 +162,7 @@
}
}
:global(.treeitem.selected) > :global(.label) {
:global([data-treeitem][aria-selected='true']) > :global([data-treeitem-label]) {
background-color: var(--color-bg-3);
}
}
@ -176,7 +176,7 @@
text-decoration: none;
padding: 0.1rem 0;
:global(.treeitem.selected) & {
:global([data-treeitem][aria-selected='true']) & {
color: var(--text-title);
}

View File

@ -21,7 +21,7 @@
{@const activeBranches = result.branches.nodes.filter(branch => branch.id !== result.defaultBranch?.id)}
{#if result.defaultBranch}
<table class="mb-3">
<table>
<thead><tr><th colspan="3">Default branch</th></tr></thead>
<tbody>
<GitReference ref={result.defaultBranch} />
@ -67,6 +67,10 @@
background-color: var(--color-bg-1);
border-radius: var(--border-radius);
border-spacing: 0;
& + table {
margin-top: 1rem;
}
}
thead th {

View File

@ -100,6 +100,12 @@
display: flex;
flex-direction: column;
}
form,
div,
:global([data-scroller]) {
padding: 1rem;
}
}
form {
@ -111,12 +117,6 @@
}
}
form,
div,
:global([data-scroller]) {
padding: 1rem;
}
form,
div,
table {

View File

@ -69,12 +69,12 @@
</ButtonGroup>
</form>
{#if !currentContributorConnection && $contributorConnection.pending}
<div class="mt-3">
<div class="info">
<LoadingSpinner />
</div>
{:else if currentContributorConnection}
{@const nodes = currentContributorConnection.nodes}
<table class="mt-3">
<table>
<tbody>
{#each nodes as contributor}
{@const commit = contributor.commits.nodes[0]}
@ -99,18 +99,16 @@
</tbody>
</table>
{#if nodes.length > 0}
<div class="d-flex flex-column align-items-center">
<div class="paginator">
<Paginator
disabled={$contributorConnection.pending}
pageInfo={currentContributorConnection.pageInfo}
/>
<p class="mt-1 text-muted">
<small>Total contributors: {currentContributorConnection.totalCount}</small>
</p>
<small>Total contributors: {currentContributorConnection.totalCount}</small>
</div>
{/if}
{:else if $contributorConnection.error}
<div class="mt-2">
<div class="info">
<Alert variant="danger">
Unable to load contributors: {$contributorConnection.error.message}
</Alert>
@ -135,6 +133,7 @@
table {
border-collapse: collapse;
width: 100%;
margin-top: 1rem;
}
td {
@ -150,4 +149,19 @@
text-overflow: ellipsis;
}
}
.paginator {
display: flex;
flex-direction: column;
align-items: center;
small {
margin-top: 0.5rem;
color: var(--text-muted);
}
}
.info {
margin-top: 1rem;
}
</style>

View File

@ -70,7 +70,7 @@
{#if entry.icon}
<Icon svgPath={entry.icon} inline />
{/if}
<span class="ml-1">{entry.title}</span>
<span>{entry.title}</span>
</a>
</li>
{/each}
@ -91,7 +91,7 @@
{#if entry.icon}
<Icon svgPath={entry.icon} inline />
{/if}
<span class="ml-1">{entry.title}</span>
<span>{entry.title}</span>
</span>
</MenuLink>
{/each}
@ -151,6 +151,7 @@
padding: 0.25rem 0.5rem;
border-radius: var(--border-radius);
white-space: nowrap;
gap: 0.25rem;
&:hover {
background-color: var(--color-bg-2);

View File

@ -78,7 +78,7 @@
</svelte:fragment>
<!-- #key is needed here to recreate the element because use:highlightCommit changes the DOM -->
{#key content}
<pre class="{highlightCls} p-2" use:highlightCommit={{ ranges: matches }}>{content}</pre>
<pre class={highlightCls} use:highlightCommit={{ ranges: matches }}>{content}</pre>
{/key}
</SearchResult>
@ -95,6 +95,7 @@
}
pre {
padding: 0.5rem;
margin: 0;
font-family: var(--code-font-family);
font-size: var(--code-font-size);

View File

@ -43,12 +43,12 @@
</span>
</div>
{#if fileSearchQueryParams}
<p class="p-2 m-0">
<p>
<a data-sveltekit-preload-data="tap" href="/search?{fileSearchQueryParams}">Show files</a>
</p>
{/if}
{#if !result.user}
<p class="p-2 m-0">
<p>
<small class="font-italic"> This owner is not associated with any Sourcegraph user </small>
</p>
{/if}
@ -60,4 +60,9 @@
margin-left: 0.5rem;
padding-left: 0.5rem;
}
p {
padding: 0.5rem;
margin: 0;
}
</style>

View File

@ -57,14 +57,14 @@
{#if description}
<!-- #key is needed here to recreate the paragraph because use:highlightRanges changes the DOM -->
{#key description}
<p class="p-2 m-0" use:highlightRanges={{ ranges: descriptionMatches }}>
<p use:highlightRanges={{ ranges: descriptionMatches }}>
{limitDescription(description)}
</p>
{/key}
{/if}<!--
Intentional weird comment to avoid adding an empty line to the body
-->{#if badges.length > 0}
<ul class="p-2">
<ul>
{#each badges as badge}
<li>
<Badge variant="outlineSecondary">
@ -81,6 +81,7 @@
<style lang="scss">
ul {
margin: 0;
padding: 0.5rem;
list-style: none;
display: flex;
gap: 0.5rem;
@ -91,6 +92,11 @@
}
}
p {
padding: 0.5rem;
margin: 0;
}
.info {
border-left: 1px solid var(--border-color);
margin-left: 0.5rem;

View File

@ -66,26 +66,15 @@
<h3>Some results skipped</h3>
{#each sortedItems as item, index (item.reason)}
{@const open = openItems[index]}
<Button variant="primary" outline>
<svelte:fragment slot="custom" let:buttonClass>
<button
type="button"
class="{buttonClass} p-2 w-100 bg-transparent border-0"
aria-expanded={open}
on:click={() => (openItems[index] = !open)}
>
<h4 class="d-flex align-items-center mb-0 w-100">
<span class="mr-1 flex-shrink-0"><Icon svgPath={icons[item.severity]} inline /></span>
<span class="flex-grow-1 text-left">{item.title}</span>
{#if item.message}
<span class="chevron flex-shrink-0"
><Icon svgPath={open ? mdiChevronDown : mdiChevronLeft} inline /></span
>
{/if}
</h4>
</button>
</svelte:fragment>
</Button>
<button type="button" class="toggle" aria-expanded={open} on:click={() => (openItems[index] = !open)}>
<h4>
<Icon svgPath={icons[item.severity]} inline --icon-fill-color="var(--primary)" />
<span class="title">{item.title}</span>
{#if item.message}
<Icon svgPath={open ? mdiChevronDown : mdiChevronLeft} inline />
{/if}
</h4>
</button>
{#if item.message && open}
<div class="message">
{@html renderMarkdown(item.message)}
@ -111,7 +100,7 @@
{/each}
<Button variant="primary">
<svelte:fragment slot="custom" let:buttonClass>
<button class="{buttonClass} mt-3" disabled={searchAgainDisabled}>
<button class="{buttonClass} search" disabled={searchAgainDisabled}>
<Icon svgPath={mdiMagnify} />
<span>Search again</span>
</button>
@ -175,4 +164,29 @@
margin: 1rem;
}
}
button.toggle {
all: unset;
cursor: pointer;
display: block;
box-sizing: border-box;
padding: 0.5rem;
width: 100%;
h4 {
display: flex;
margin-bottom: 0;
align-items: center;
gap: 0.25rem;
.title {
flex: 1;
}
}
}
button.search {
margin-top: 1rem;
}
</style>

View File

@ -32,7 +32,7 @@
</span>
</div>
{#if fileSearchQueryParams}
<p class="p-2 m-0">
<p>
<a data-sveltekit-preload-data="tap" href="/search?{fileSearchQueryParams}">Show files</a>
</p>
{/if}
@ -44,4 +44,9 @@
margin-left: 0.5rem;
padding-left: 0.5rem;
}
p {
padding: 0.5rem;
margin: 0;
}
</style>

View File

@ -1,4 +1,15 @@
@import 'wildcard/src/global-styles/base.scss';
@import 'wildcard/src/global-styles/colors';
@import 'wildcard/src/global-styles/variables';
@import 'wildcard/src/global-styles/reboot';
@import '$lib/styles/utilities';
@import '$lib/styles/dropdown';
@import '$lib/styles/popover';
@import 'wildcard/src/global-styles/typography';
@import 'wildcard/src/global-styles/tables';
@import 'wildcard/src/global-styles/code';
@import 'wildcard/src/global-styles/forms';
@import 'svelte-overrides.scss';
.match-highlight {
@ -11,6 +22,10 @@
// links, buttons, inputs, etc. We have to have some common "global"
// styles for them (like focus ring).
:focus {
outline: none;
}
a:focus-visible {
// Inset is required here since we have a lot of links
// that don't have any spacing between their border and

View File

@ -37,7 +37,7 @@ export default defineConfig(({ mode }) => {
// (without it scss @import paths are always relative to the importing file)
join(__dirname, '..'),
],
additionalData: `@use '$lib/breakpoints.scss';`,
additionalData: `@use '$lib/styles/breakpoints.scss';`,
},
},
modules: {