Fix several CSS compatibility issues in Safari (#841)

* Fix button margin default for Safari compatibility

* Add Safari vendor prefixes that are somehow still necessary

* Add workaround for Safari not rendering text selection

* Replace <h3> and <p> placeholder labels in floating menus with <TextLabel>

* Replace <span> elements with <TextLabel> and set its cursor for Safari
This commit is contained in:
Keavon Chambers 2022-11-06 00:50:45 -07:00
parent 3f98d1c896
commit 4c16efb33d
25 changed files with 92 additions and 69 deletions

View File

@ -78,8 +78,9 @@ body,
margin: 0;
height: 100%;
background: var(--color-2-mildblack);
user-select: none;
overscroll-behavior: none;
-webkit-user-select: none; // Required as of Safari 15.0 (Graphite's minimum version) through the latest release
user-select: none;
}
html,
@ -223,27 +224,6 @@ img {
outline: 1px dashed var(--color-2-mildblack);
}
}
// For placeholder messages (remove eventually)
.floating-menu {
h1,
h2,
h3,
h4,
h5,
h6,
p {
margin: 0;
}
p {
margin-top: 8px;
}
.floating-menu-content h3 ~ p {
white-space: pre-wrap;
}
}
</style>
<script lang="ts">

View File

@ -300,6 +300,7 @@
.preset-color {
border: none;
margin: 0;
padding: 0;
border-radius: 2px;
width: calc(48px + (48px + 4px) / 2);

View File

@ -51,6 +51,7 @@
margin: -4px 0;
.details.text-label {
-webkit-user-select: text; // Required as of Safari 15.0 (Graphite's minimum version) through the latest release
user-select: text;
white-space: pre-wrap;
max-width: 400px;

View File

@ -31,7 +31,7 @@
<link v-if="entry.font" rel="stylesheet" :href="entry.font?.toString()" />
<span class="entry-label" :style="{ fontFamily: `${!entry.font ? 'inherit' : entry.value}` }">{{ entry.label }}</span>
<TextLabel class="entry-label" :style="{ fontFamily: `${!entry.font ? 'inherit' : entry.value}` }">{{ entry.label }}</TextLabel>
<UserInputLabel v-if="entry.shortcut?.keys.length" :keysWithLabelsGroups="[entry.shortcut.keys]" :requiresLock="entry.shortcutRequiresLock" />
@ -171,6 +171,7 @@ import LayoutCol from "@/components/layout/LayoutCol.vue";
import LayoutRow from "@/components/layout/LayoutRow.vue";
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
import Separator from "@/components/widgets/labels/Separator.vue";
import TextLabel from "@/components/widgets/labels/TextLabel.vue";
import UserInputLabel from "@/components/widgets/labels/UserInputLabel.vue";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -350,6 +351,7 @@ const MenuList = defineComponent({
LayoutCol,
LayoutRow,
Separator,
TextLabel,
UserInputLabel,
},
});

View File

@ -199,8 +199,8 @@
overflow: visible;
div {
background: none;
cursor: text;
background: none;
border: none;
margin: 0;
padding: 0;

View File

@ -125,10 +125,11 @@
}
.expand-arrow {
padding: 0;
margin: 0;
margin-left: -16px;
width: 16px;
height: 100%;
padding: 0;
border: none;
position: relative;
background: none;
@ -204,6 +205,7 @@
width: 100%;
&:disabled {
-webkit-user-select: none; // Required as of Safari 15.0 (Graphite's minimum version) through the latest release
user-select: none;
// Workaround for `user-select: none` not working on <input> elements
pointer-events: none;

View File

@ -8,7 +8,12 @@
@pointerdown="(e: PointerEvent) => pointerDown(e)"
@pointermove="(e: PointerEvent) => pointerMove(e)"
@pointerup="(e: PointerEvent) => pointerUp(e)"
:style="`--grid-spacing: ${gridSpacing}px; --grid-offset-x: ${transform.x * transform.scale}px; --grid-offset-y: ${transform.y * transform.scale}px; --dot-radius: ${dotRadius}px`"
:style="{
'--grid-spacing': `${gridSpacing}px`,
'--grid-offset-x': `${transform.x * transform.scale}px`,
'--grid-offset-y': `${transform.y * transform.scale}px`,
'--dot-radius': `${dotRadius}px`,
}"
>
<div
class="nodes"

View File

@ -39,8 +39,8 @@
<OptionalInput v-if="component.props.kind === 'OptionalInput'" v-bind="component.props" @update:checked="(value: boolean) => updateLayout(component.widgetId, value)" />
<PivotAssist v-if="component.props.kind === 'PivotAssist'" v-bind="component.props" @update:position="(value: string) => updateLayout(component.widgetId, value)" />
<PopoverButton v-if="component.props.kind === 'PopoverButton'" v-bind="component.props">
<h3>{{ (component.props as any).header }}</h3>
<p>{{ (component.props as any).text }}</p>
<TextLabel :bold="true">{{ (component.props as any).header }}</TextLabel>
<TextLabel :multiline="true">{{ (component.props as any).text }}</TextLabel>
</PopoverButton>
<RadioInput
v-if="component.props.kind === 'RadioInput'"

View File

@ -1,6 +1,7 @@
<template>
<button
:class="['icon-button', `size-${size}`, { disabled, active, 'sharp-right-corners': sharpRightCorners }]"
class="icon-button"
:class="[`size-${size}`, { disabled, active, 'sharp-right-corners': sharpRightCorners }]"
@click="(e: MouseEvent) => action(e)"
:disabled="disabled"
:title="tooltip"
@ -16,6 +17,7 @@
justify-content: center;
align-items: center;
flex: 0 0 auto;
margin: 0;
padding: 0;
border: none;
border-radius: 2px;

View File

@ -6,7 +6,7 @@
:data-disabled="disabled || undefined"
data-text-button
:title="tooltip"
:style="minWidth > 0 ? `min-width: ${minWidth}px` : ''"
:style="{ 'min-width': minWidth > 0 ? `${minWidth}px` : undefined }"
@click="(e: MouseEvent) => action(e)"
:tabindex="disabled ? -1 : 0"
>
@ -22,6 +22,7 @@
align-items: center;
flex: 0 0 auto;
height: 24px;
margin: 0;
padding: 0 8px;
box-sizing: border-box;
border: none;

View File

@ -29,6 +29,7 @@
.expand-arrow {
width: 6px;
height: 100%;
margin: 0;
padding: 0;
position: relative;
flex: 0 0 auto;

View File

@ -25,8 +25,8 @@
position: relative;
overflow: hidden;
border: none;
padding: 0;
margin: 0;
padding: 0;
width: 100%;
height: 100%;
border-radius: 1px;

View File

@ -12,7 +12,7 @@
data-floating-menu-spawner
>
<IconLabel class="dropdown-icon" :icon="activeEntry.icon" v-if="activeEntry.icon" />
<span>{{ activeEntry.label }}</span>
<TextLabel class="dropdown-label">{{ activeEntry.label }}</TextLabel>
<IconLabel class="dropdown-arrow" :icon="'DropdownArrow'" />
</LayoutRow>
<MenuList
@ -40,20 +40,20 @@
height: 24px;
border-radius: 2px;
.dropdown-icon {
margin: 4px;
flex: 0 0 auto;
}
span {
.dropdown-label {
margin: 0;
margin-left: 8px;
flex: 1 1 100%;
}
.dropdown-icon + span {
.dropdown-icon {
margin: 4px;
flex: 0 0 auto;
& + .dropdown-label {
margin-left: 0;
}
}
.dropdown-arrow {
margin: 6px 2px;
@ -104,6 +104,7 @@ import { type MenuListEntry } from "@/wasm-communication/messages";
import MenuList from "@/components/floating-menus/MenuList.vue";
import LayoutRow from "@/components/layout/LayoutRow.vue";
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
import TextLabel from "@/components/widgets/labels/TextLabel.vue";
const DASH_ENTRY = { label: "-" };
@ -167,6 +168,7 @@ export default defineComponent({
IconLabel,
LayoutRow,
MenuList,
TextLabel,
},
});
</script>

View File

@ -80,7 +80,16 @@
caret-color: var(--color-e-nearwhite);
&::selection {
background: var(--color-5-dullgray);
background-color: var(--color-5-dullgray);
// Target only Safari
@supports (background: -webkit-named-image(i)) {
& {
// Setting an alpha value opts out of Safari's "fancy" (but not visible on dark backgrounds) selection highlight rendering
// https://stackoverflow.com/a/71753552/775283
background-color: rgba(var(--color-5-dullgray-rgb), calc(254 / 255));
}
}
}
}

View File

@ -12,7 +12,7 @@
@keydown="keydown"
data-floating-menu-spawner
>
<span>{{ activeEntry?.value || "" }}</span>
<TextLabel class="dropdown-label">{{ activeEntry?.value || "" }}</TextLabel>
<IconLabel class="dropdown-arrow" :icon="'DropdownArrow'" />
</LayoutRow>
<MenuList
@ -39,7 +39,7 @@
height: 24px;
border-radius: 2px;
span {
.dropdown-label {
margin: 0;
margin-left: 8px;
flex: 1 1 100%;
@ -87,6 +87,7 @@ import { type MenuListEntry } from "@/wasm-communication/messages";
import MenuList from "@/components/floating-menus/MenuList.vue";
import LayoutRow from "@/components/layout/LayoutRow.vue";
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
import TextLabel from "@/components/widgets/labels/TextLabel.vue";
export default defineComponent({
inject: ["fonts"],
@ -186,6 +187,7 @@ export default defineComponent({
IconLabel,
LayoutRow,
MenuList,
TextLabel,
},
});
</script>

View File

@ -11,7 +11,7 @@
:data-floating-menu-spawner="entry.children && entry.children.length > 0 ? '' : 'no-hover-transfer'"
>
<IconLabel v-if="entry.icon" :icon="entry.icon" />
<span v-if="entry.label">{{ entry.label }}</span>
<TextLabel v-if="entry.label">{{ entry.label }}</TextLabel>
</div>
<MenuList
v-if="entry.children && entry.children.length > 0"
@ -72,6 +72,7 @@ import { type KeyRaw, type KeysGroup, type MenuBarEntry, type MenuListEntry, Upd
import MenuList from "@/components/floating-menus/MenuList.vue";
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
import TextLabel from "@/components/widgets/labels/TextLabel.vue";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
type MenuListInstance = InstanceType<typeof MenuList>;
@ -147,6 +148,7 @@ export default defineComponent({
components: {
IconLabel,
MenuList,
TextLabel,
},
});
</script>

View File

@ -59,6 +59,7 @@
.arrow {
position: absolute;
top: 0;
margin: 0;
padding: 9px 0;
border: none;
background: rgba(var(--color-1-nearblack-rgb), 0.75);
@ -131,7 +132,7 @@
height: 100%;
padding: 0;
margin: 0;
-webkit-appearance: none; // TODO: Prefix necessary? Test on Safari
-webkit-appearance: none; // Required until Safari 15.4 (Graphite supports 15.0+)
appearance: none;
background: none;
cursor: default;
@ -147,7 +148,7 @@
// Chromium and Safari
&::-webkit-slider-thumb {
-webkit-appearance: none; // TODO: Prefix necessary? Test on Safari
-webkit-appearance: none; // Required until Safari 15.4 (Graphite supports 15.0+)
appearance: none;
border-radius: 2px;
width: 4px;

View File

@ -21,6 +21,7 @@
background: var(--color-5-dullgray);
fill: var(--color-e-nearwhite);
height: 24px;
margin: 0;
padding: 0 4px;
border: none;
display: flex;

View File

@ -1,11 +1,11 @@
<template>
<LayoutCol class="swatch-pair">
<LayoutRow class="primary swatch">
<button @click="() => clickPrimarySwatch()" :style="`--swatch-color: ${primary.toRgbaCSS()}`" data-floating-menu-spawner="no-hover-transfer" tabindex="0"></button>
<button @click="() => clickPrimarySwatch()" :style="{ '--swatch-color': primary.toRgbaCSS() }" data-floating-menu-spawner="no-hover-transfer" tabindex="0"></button>
<ColorPicker v-model:open="primaryOpen" :color="primary" @update:color="(color: Color) => primaryColorChanged(color)" :direction="'Right'" />
</LayoutRow>
<LayoutRow class="secondary swatch">
<button @click="() => clickSecondarySwatch()" :style="`--swatch-color: ${secondary.toRgbaCSS()}`" data-floating-menu-spawner="no-hover-transfer" tabindex="0"></button>
<button @click="() => clickSecondarySwatch()" :style="{ '--swatch-color': secondary.toRgbaCSS() }" data-floating-menu-spawner="no-hover-transfer" tabindex="0"></button>
<ColorPicker v-model:open="secondaryOpen" :color="secondary" @update:color="(color: Color) => secondaryColorChanged(color)" :direction="'Right'" />
</LayoutRow>
</LayoutCol>

View File

@ -7,7 +7,7 @@
:spellcheck="true"
:disabled="disabled"
:tooltip="tooltip"
:style="minWidth > 0 ? `min-width: ${minWidth}px` : ''"
:style="{ 'min-width': minWidth > 0 ? `${minWidth}px` : undefined }"
:sharpRightCorners="sharpRightCorners"
@textFocused="() => onTextFocused()"
@textChanged="() => onTextChanged()"

View File

@ -1,5 +1,5 @@
<template>
<span class="text-label" :class="{ disabled, bold, italic, multiline, 'table-align': tableAlign }" :style="minWidth > 0 ? `min-width: ${minWidth}px` : ''" :title="tooltip">
<span class="text-label" :class="{ disabled, bold, italic, multiline, 'table-align': tableAlign }" :style="{ 'min-width': minWidth > 0 ? `${minWidth}px` : undefined }" :title="tooltip">
<slot></slot>
</span>
</template>
@ -8,6 +8,8 @@
.text-label {
line-height: 18px;
white-space: nowrap;
// Force Safari to not draw a text cursor, even though this element has `user-select: none`
cursor: default;
&.disabled {
color: var(--color-8-uppergray);

View File

@ -2,20 +2,20 @@
<IconLabel class="user-input-label keyboard-lock-notice" v-if="displayKeyboardLockNotice" :icon="'Info'" :title="keyboardLockInfoMessage" />
<LayoutRow class="user-input-label" v-else>
<template v-for="(keysWithLabels, i) in keysWithLabelsGroups" :key="i">
<span class="group-gap" v-if="i > 0"></span>
<Separator :type="'Related'" v-if="i > 0"></Separator>
<template v-for="(keyInfo, j) in keyTextOrIconList(keysWithLabels)" :key="j">
<span class="input-key" :class="keyInfo.width">
<div class="input-key" :class="keyInfo.width">
<IconLabel v-if="keyInfo.icon" :icon="keyInfo.icon" />
<template v-else-if="keyInfo.label !== undefined">{{ keyInfo.label }}</template>
</span>
<TextLabel v-else-if="keyInfo.label !== undefined">{{ keyInfo.label }}</TextLabel>
</div>
</template>
</template>
<span class="input-mouse" v-if="mouseMotion">
<div class="input-mouse" v-if="mouseMotion">
<IconLabel :icon="mouseHintIcon(mouseMotion)" />
</span>
<span class="hint-text" v-if="hasSlotContent">
</div>
<div class="hint-text" v-if="hasSlotContent">
<slot></slot>
</span>
</div>
</LayoutRow>
</template>
@ -26,10 +26,6 @@
align-items: center;
white-space: nowrap;
.group-gap {
width: 4px;
}
.input-key,
.input-mouse {
& + .input-key,
@ -46,14 +42,18 @@
font-weight: 400;
text-align: center;
height: 16px;
// Firefox renders the text 1px lower than Chrome (tested on Windows) with 16px line-height, so moving it up 1 pixel by using 15px makes them agree
line-height: 15px;
box-sizing: border-box;
border: 1px solid;
border-radius: 4px;
border-color: var(--color-5-dullgray);
color: var(--color-e-nearwhite);
.text-label {
// Firefox renders the text 1px lower than Chrome (tested on Windows) with 16px line-height,
// so moving it up 1 pixel by using 15px makes them agree.
line-height: 15px;
}
&.width-1 {
width: 16px;
}
@ -131,6 +131,8 @@ import { type KeyRaw, type KeysGroup, type Key, type MouseMotion } from "@/wasm-
import LayoutRow from "@/components/layout/LayoutRow.vue";
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
import Separator from "@/components/widgets/labels/Separator.vue";
import TextLabel from "@/components/widgets/labels/TextLabel.vue";
type LabelData = { label?: string; icon?: IconName; width: string };
@ -240,6 +242,8 @@ export default defineComponent({
components: {
IconLabel,
LayoutRow,
Separator,
TextLabel,
},
});
</script>

View File

@ -20,6 +20,7 @@
border-style: solid;
width: 0;
height: 0;
margin: 0;
padding: 0;
}

View File

@ -1,6 +1,6 @@
<template>
<LayoutRow class="window-title">
<span>{{ text }}</span>
<TextLabel>{{ text }}</TextLabel>
</LayoutRow>
</template>
@ -17,11 +17,15 @@
import { defineComponent, type PropType } from "vue";
import LayoutRow from "@/components/layout/LayoutRow.vue";
import TextLabel from "@/components/widgets/labels/TextLabel.vue";
export default defineComponent({
props: {
text: { type: String as PropType<string>, required: true },
},
components: { LayoutRow },
components: {
LayoutRow,
TextLabel,
},
});
</script>

View File

@ -12,13 +12,13 @@
@click.middle="(e: MouseEvent) => (e?.stopPropagation(), closeAction?.(tabIndex))"
data-tab
>
<span>{{ tabLabel.name }}</span>
<TextLabel>{{ tabLabel.name }}</TextLabel>
<IconButton :action="(e?: MouseEvent) => (e?.stopPropagation(), closeAction?.(tabIndex))" :icon="'CloseX'" :size="16" v-if="tabCloseButtons" />
</LayoutRow>
</LayoutRow>
<PopoverButton :icon="'VerticalEllipsis'">
<h3>Panel Options</h3>
<p>The contents of this popover menu are coming soon</p>
<TextLabel :bold="true">Panel Options</TextLabel>
<TextLabel :multiline="true">The contents of this popover menu are coming soon</TextLabel>
</PopoverButton>
</LayoutRow>
<LayoutCol class="panel-body">