This commit is contained in:
Jamie Pine 2022-10-20 21:55:39 -07:00
parent 90a267930a
commit a36a30e317
15 changed files with 253 additions and 183 deletions

View file

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" class="vanilla-dark">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />

View file

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html class="dark">
<html class="vanilla-light">
<head>
<meta charset="utf-8" />
<title>Spacedrive</title>

View file

@ -12,7 +12,7 @@
"desktop": "pnpm --filter @sd/desktop --",
"web": "pnpm --filter @sd/web -- ",
"mobile": "pnpm --filter @sd/mobile --",
"server": "pnpm --filter @sd/server -- ",
"core": "pnpm --filter @sd/server -- ",
"landing": "pnpm --filter @sd/landing -- ",
"ui": "pnpm --filter @sd/ui -- ",
"interface": "pnpm --filter @sd/interface -- ",

View file

@ -1,12 +1,11 @@
import * as ToastPrimitive from '@radix-ui/react-toast';
import { useCurrentLibrary } from '@sd/client';
import clsx from 'clsx';
import { Suspense, useEffect, useState } from 'react';
import { Suspense } from 'react';
import { Outlet } from 'react-router-dom';
import { Sidebar } from './components/layout/Sidebar';
import { Toasts } from './components/primitive/Toasts';
import { useOperatingSystem } from './hooks/useOperatingSystem';
import { useToasts } from './hooks/useToasts';
export function AppLayout() {
const { libraries } = useCurrentLibrary();
@ -19,19 +18,20 @@ export function AppLayout() {
return (
<div
className={clsx(
// App level styles
'flex flex-row overflow-hidden text-ink select-none cursor-default',
os === 'macOS' && 'rounded-xl',
os !== 'browser' && os !== 'windows' && 'border border-app-divider'
)}
onContextMenu={(e) => {
// TODO: allow this on some UI text at least / disable default browser context menu
e.preventDefault();
return false;
}}
className={clsx(
'flex flex-row h-screen overflow-hidden text-gray-900 select-none dark:text-white cursor-default',
os === 'macOS' && 'rounded-xl',
os !== 'browser' && os !== 'windows' && 'border border-gray-200 dark:border-gray-500'
)}
>
<Sidebar />
<div className="relative flex w-full h-screen max-h-screen bg-white dark:bg-gray-650">
<div className="relative flex w-full h-screen max-h-screen">
<Suspense>
<Outlet />
</Suspense>
@ -40,90 +40,3 @@ export function AppLayout() {
</div>
);
}
function Toasts() {
const { toasts, addToast, removeToast } = useToasts();
// useEffect(() => {
// setTimeout(() => {
// addToast({
// title: 'Spacedrop',
// subtitle: 'Someone tried to send you a file. Accept it?',
// actionButton: {
// text: 'Accept',
// onClick: () => {
// console.log('Bruh');
// }
// }
// });
// }, 2000);
// }, []);
return (
<div className="fixed right-0 flex">
<ToastPrimitive.Provider>
<>
{toasts.map((toast) => (
<ToastPrimitive.Root
key={toast.id}
open={true}
onOpenChange={() => removeToast(toast)}
duration={toast.duration || 3000}
className={clsx(
'w-80 m-4 shadow-lg rounded-lg',
'bg-gray-800/20 backdrop-blur',
'radix-state-open:animate-toast-slide-in-bottom md:radix-state-open:animate-toast-slide-in-right',
'radix-state-closed:animate-toast-hide',
'radix-swipe-end:animate-toast-swipe-out',
'translate-x-radix-toast-swipe-move-x',
'radix-swipe-cancel:translate-x-0 radix-swipe-cancel:duration-200 radix-swipe-cancel:ease-[ease]',
'focus:outline-none focus-visible:ring focus-visible:ring-primary focus-visible:ring-opacity-75 border-white/10 border-2 shadow-2xl'
)}
>
<div className="flex">
<div className="flex items-center flex-1 w-0 py-4 pl-5">
<div className="w-full radix">
<ToastPrimitive.Title className="text-sm font-medium text-gray-900 dark:text-gray-100">
{toast.title}
</ToastPrimitive.Title>
{toast.subtitle && (
<ToastPrimitive.Description className="mt-1 text-sm text-gray-700 dark:text-gray-400">
{toast.subtitle}
</ToastPrimitive.Description>
)}
</div>
</div>
<div className="flex">
<div className="flex flex-col px-3 py-2 space-y-1">
<div className="flex flex-1 h-0">
{toast.actionButton && (
<ToastPrimitive.Action
altText="view now"
className="flex items-center justify-center w-full px-3 py-2 text-sm font-medium border border-transparent rounded-lg text-primary dark:text-primary hover:bg-white/10 focus:z-10 focus:outline-none focus-visible:ring focus-visible:ring-primary focus-visible:ring-opacity-75"
onClick={(e) => {
e.preventDefault();
toast.actionButton?.onClick();
removeToast(toast);
}}
>
{toast.actionButton.text || 'Open'}
</ToastPrimitive.Action>
)}
</div>
<div className="flex flex-1 h-0">
<ToastPrimitive.Close className="flex items-center justify-center w-full px-3 py-2 text-sm font-medium text-gray-700 border border-transparent rounded-lg dark:text-gray-100 hover:bg-white/10 focus:z-10 focus:outline-none focus-visible:ring focus-visible:ring-primary focus-visible:ring-opacity-75">
Dismiss
</ToastPrimitive.Close>
</div>
</div>
</div>
</div>
</ToastPrimitive.Root>
))}
<ToastPrimitive.Viewport />
</>
</ToastPrimitive.Provider>
</div>
);
}

View file

@ -12,9 +12,9 @@ export function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
<div
data-tauri-drag-region
role="alert"
className="flex flex-col items-center justify-center w-screen h-screen p-4 border border-gray-200 rounded-lg dark:border-gray-650 bg-gray-50 dark:bg-gray-650 dark:text-white"
className="flex flex-col items-center justify-center w-screen h-screen p-4 border rounded-lg border-app-divider bg-app"
>
<p className="m-3 text-sm font-bold text-gray-400">APP CRASHED</p>
<p className="m-3 text-sm font-bold text-ink-faint">APP CRASHED</p>
<h1 className="text-2xl font-bold">We're past the event horizon...</h1>
<pre className="m-2">Error: {error.message}</pre>
<div className="flex flex-row space-x-2">

View file

@ -7,9 +7,9 @@ export default function NotFound() {
<div
data-tauri-drag-region
role="alert"
className="flex flex-col items-center justify-center w-full h-full p-4 rounded-lg dark:text-white"
className="flex flex-col items-center justify-center w-full h-full p-4 rounded-lg"
>
<p className="m-3 text-sm font-semibold text-gray-500 uppercase">Error: 404</p>
<p className="m-3 text-sm font-semibold uppercase text-ink-faint">Error: 404</p>
<h1 className="text-4xl font-bold">You chose nothingness.</h1>
<div className="flex flex-row space-x-2">
<Button variant="primary" className="mt-4" onClick={() => navigate(-1)}>

View file

@ -26,7 +26,7 @@ export function Device(props: DeviceProps) {
}
return (
<div className="w-full border border-gray-100 rounded-md bg-gray-50 dark:bg-gray-600 dark:border-gray-550">
<div className="w-full border rounded-md border-app-divider bg-app">
<div className="flex flex-row items-center px-4 pt-2 pb-2">
<DotsSixVertical weight="bold" className="mr-3 opacity-30" />
{props.type === 'phone' && <DeviceMobileCamera weight="fill" size={20} className="mr-2" />}
@ -35,7 +35,7 @@ export function Device(props: DeviceProps) {
{props.type === 'server' && <Cloud weight="fill" size={20} className="mr-2" />}
<h3 className="font-semibold text-md">{props.name || 'Unnamed Device'}</h3>
<div className="flex flex-row space-x-1.5 mt-0.5">
<span className="font-semibold flex flex-row h-[19px] -mt-0.5 ml-3 py-0.5 px-1.5 text-[10px] rounded bg-gray-250 text-gray-500 dark:bg-gray-500 dark:text-gray-400">
<span className="font-semibold flex flex-row h-[19px] -mt-0.5 ml-3 py-0.5 px-1.5 text-[10px] rounded bg-gray-250 text-type-faint">
<LockClosedIcon className="w-3 h-3 mr-1 -ml-0.5 m-[1px]" />
P2P
</span>

View file

@ -22,7 +22,7 @@ export default function Explorer(props: Props) {
return (
<div className="relative">
<ExplorerContextMenu>
<div className="relative flex flex-col w-full bg-gray-650">
<div className="relative flex flex-col w-full dark:bg-gray-650">
<TopBar />
<div className="relative flex flex-row w-full max-h-full ">

View file

@ -21,10 +21,9 @@ export const SidebarLink = (props: PropsWithChildren<NavLinkProps>) => (
{({ isActive }) => (
<span
className={clsx(
'max-w mb-[2px] text-gray-550 dark:text-gray-300 rounded px-2 py-1 flex flex-row flex-grow items-center font-medium text-sm',
'max-w mb-[2px] text-gray-550 rounded px-2 py-1 flex flex-row flex-grow items-center font-medium text-sm',
{
'!bg-gray-400 !bg-opacity-10 !text-white hover:bg-gray-400 dark:hover:bg-gray-400':
isActive
'bg-sidebar-selected': isActive
},
props.className
)}
@ -82,15 +81,10 @@ function LibraryScopedSection() {
{({ isActive }) => (
<span
className={clsx(
'max-w mb-[2px] text-gray-550 dark:text-gray-150 rounded px-2 py-1 gap-2 flex flex-row flex-grow items-center truncate text-sm',
{
'!bg-gray-400 !bg-opacity-10 !text-white hover:bg-gray-400 dark:hover:bg-gray-400':
isActive
}
'max-w mb-[2px] rounded px-2 py-1 gap-2 flex flex-row flex-grow items-center truncate text-sm'
)}
>
<div className="-mt-0.5 flex-grow-0 flex-shrink-0">
{/* <Folder size={18} className={clsx(!isActive && 'hidden')} white /> */}
<Folder size={18} />
</div>
@ -102,7 +96,7 @@ function LibraryScopedSection() {
);
})}
{(locations?.length || 0) < 1 && (
{(locations?.length || 0) < 4 && (
<button
onClick={() => {
if (!platform.openFilePickerDialog) {
@ -110,7 +104,6 @@ function LibraryScopedSection() {
alert('Opening a dialogue is not supported on this platform!');
return;
}
platform.openFilePickerDialog().then((result) => {
// TODO: Pass indexer rules ids to create location
if (result)
@ -121,10 +114,10 @@ function LibraryScopedSection() {
});
}}
className={clsx(
'w-full px-2 py-1.5 mt-1 text-xs font-bold text-center text-gray-400 border border-dashed rounded border-transparent cursor-normal border-gray-350 transition',
os === 'macOS'
? 'dark:text-gray-450 dark:border-gray-450 hover:dark:border-gray-400 dark:border-opacity-60'
: 'dark:text-gray-450 dark:border-gray-550 hover:dark:border-gray-500'
'w-full px-2 py-1.5 mt-1 text-xs font-bold text-center text-ink-faint border border-dashed rounded border-sidebar-box cursor-normal transition'
// os === 'macOS'
// ? 'dark:text-gray-450 dark:border-gray-450 hover:dark:border-gray-400 dark:border-opacity-60'
// : 'dark:text-gray-450 dark:border-gray-550 hover:dark:border-gray-500'
)}
>
Add Location
@ -163,8 +156,8 @@ export function Sidebar() {
return (
<div
className={clsx(
'flex flex-col flex-grow-0 flex-shrink-0 w-48 min-h-full px-2.5 overflow-x-hidden overflow-y-scroll border-r border-gray-100 no-scrollbar bg-gray-50 dark:bg-gray-850 dark:border-gray-750',
macOnly(os, 'dark:!bg-opacity-30')
'flex flex-col flex-grow-0 flex-shrink-0 w-48 min-h-full px-2.5 overflow-x-hidden overflow-y-scroll border-r border-sidebar-divider no-scrollbar bg-sidebar/100'
// macOnly(os, 'bg-sidebar/30')
)}
>
<WindowControls />

View file

@ -0,0 +1,75 @@
import * as ToastPrimitive from '@radix-ui/react-toast';
import clsx from 'clsx';
import { useToasts } from '../../hooks/useToasts';
export function Toasts() {
const { toasts, addToast, removeToast } = useToasts();
return (
<div className="fixed right-0 flex">
<ToastPrimitive.Provider>
<>
{toasts.map((toast) => (
<ToastPrimitive.Root
key={toast.id}
open={true}
onOpenChange={() => removeToast(toast)}
duration={toast.duration || 3000}
className={clsx(
'w-80 m-4 shadow-lg rounded-lg',
'bg-app-box/20 backdrop-blur',
'radix-state-open:animate-toast-slide-in-bottom md:radix-state-open:animate-toast-slide-in-right',
'radix-state-closed:animate-toast-hide',
'radix-swipe-end:animate-toast-swipe-out',
'translate-x-radix-toast-swipe-move-x',
'radix-swipe-cancel:translate-x-0 radix-swipe-cancel:duration-200 radix-swipe-cancel:ease-[ease]',
'focus:outline-none focus-visible:ring focus-visible:ring-primary focus-visible:ring-opacity-75 border-white/10 border-2 shadow-2xl'
)}
>
<div className="flex">
<div className="flex items-center flex-1 w-0 py-4 pl-5">
<div className="w-full radix">
<ToastPrimitive.Title className="text-sm font-medium text-black">
{toast.title}
</ToastPrimitive.Title>
{toast.subtitle && (
<ToastPrimitive.Description className="mt-1 text-sm text-black">
{toast.subtitle}
</ToastPrimitive.Description>
)}
</div>
</div>
<div className="flex">
<div className="flex flex-col px-3 py-2 space-y-1">
<div className="flex flex-1 h-0">
{toast.actionButton && (
<ToastPrimitive.Action
altText="view now"
className="flex items-center justify-center w-full px-3 py-2 text-sm font-medium border border-transparent rounded-lg text-accent hover:bg-white/10 focus:z-10 focus:outline-none focus-visible:ring focus-visible:ring-primary focus-visible:ring-opacity-75"
onClick={(e) => {
e.preventDefault();
toast.actionButton?.onClick();
removeToast(toast);
}}
>
{toast.actionButton.text || 'Open'}
</ToastPrimitive.Action>
)}
</div>
<div className="flex flex-1 h-0">
<ToastPrimitive.Close className="flex items-center justify-center w-full px-3 py-2 text-sm font-medium border border-transparent rounded-lg text-ink-faint hover:bg-white/10 focus:z-10 focus:outline-none focus-visible:ring focus-visible:ring-primary focus-visible:ring-opacity-75">
Dismiss
</ToastPrimitive.Close>
</div>
</div>
</div>
</div>
</ToastPrimitive.Root>
))}
<ToastPrimitive.Viewport />
</>
</ToastPrimitive.Provider>
</div>
);
}

View file

@ -32,7 +32,7 @@ body {
@apply bg-[#00000006] dark:bg-[#00000000] mt-[53px] rounded-[6px];
}
&::-webkit-scrollbar-thumb {
@apply rounded-[6px] bg-gray-300 dark:bg-gray-550;
@apply rounded-[6px] bg-app-box;
}
}
.default-scroll {
@ -44,7 +44,7 @@ body {
@apply bg-transparent rounded-[6px];
}
&::-webkit-scrollbar-thumb {
@apply rounded-[6px] bg-gray-300 dark:bg-gray-550;
@apply rounded-[6px] bg-app-box;
}
}
.page-scroll {
@ -56,7 +56,7 @@ body {
@apply bg-transparent rounded-[6px] my-[10px];
}
&::-webkit-scrollbar-thumb {
@apply rounded-[6px] bg-gray-300 dark:bg-gray-550;
@apply rounded-[6px] bg-app-box;
}
}
.inspector-scroll {
@ -69,7 +69,7 @@ body {
@apply bg-transparent my-[8px];
}
&::-webkit-scrollbar-thumb {
@apply rounded-[6px] opacity-0 bg-gray-300 dark:bg-gray-950;
@apply rounded-[6px] opacity-0 bg-app/70;
}
&:hover {
&::-webkit-scrollbar-thumb {
@ -88,7 +88,7 @@ body {
@apply bg-transparent my-[5px];
}
&::-webkit-scrollbar-thumb {
@apply rounded-[6px] opacity-0 bg-gray-300 dark:bg-gray-950 w-[5px];
@apply rounded-[6px] opacity-0 bg-black/70 w-[5px];
}
&:hover {
&::-webkit-scrollbar-thumb {

View file

@ -1,3 +1,3 @@
import { tw } from './utils';
export const CategoryHeading = tw.h3`mt-1 mb-1 text-xs font-semibold text-gray-300`;
export const CategoryHeading = tw.h3`mt-1 mb-1 text-xs font-semibold text-ink-dull`;

View file

@ -0,0 +1,67 @@
// Notes
// shadow should be used as shadow-black/40
.vanilla-dark {
// global
--color-black: 0, 0, 0;
--color-white: 255, 255, 255;
// accent theme colors
--color-accent: 0, 0, 0;
--color-accent-faint: 0, 0, 0;
--color-accent-deep: 0, 0, 0;
// text
--color-ink: 255, 255, 255;
--color-ink-dull: 190, 190, 190;
--color-ink-faint: 150, 150, 150;
// sidebar
--color-sidebar: 13, 17, 23;
--color-sidebar-box: 48,53,68;
--color-sidebar-border: 48,53,68;
--color-sidebar-divider: 48,53,68;
--color-sidebar-button: 48,53,68;
--color-sidebar-selected: 48,53,68;
--color-sidebar-separator: 48,53,68;
// main
--color-app: 0, 0, 0;
--color-app-box: 0, 0, 0;
--color-app-input: 0, 0, 0;
--color-app-border: 0, 0, 0;
--color-app-button: 0, 0, 0;
--color-app-divider: 0, 0, 0;
--color-app-selected: 0, 0, 0;
--color-app-hover: 0, 0, 0;
--color-app-separator: 0, 0, 0;
}
.vanilla-light {
// global
--color-black: 0,0,0;
--color-white: 255,255,255;
// accent theme colors
--color-accent: 0,0,0;
--color-accent-faint: 0,0,0;
--color-accent-deep: 0,0,0;
// text
--color-ink: 0,0,0;
--color-ink-dull: 35,35,35;
--color-ink-faint: 55,55,55;
// sidebar
--color-sidebar: 255,255,255;
--color-sidebar-box: 233,237,253;
--color-sidebar-border: 233,237,253;
--color-sidebar-divider: 233,237,253;
--color-sidebar-button: 233,237,253;
--color-sidebar-selected: 233,237,253;
--color-sidebar-separator: 233,237,253;
// main
--color-app: 0,0,0;
--color-app-box: 0,0,0;
--color-app-input: 0,0,0;
--color-app-border: 0,0,0;
--color-app-button: 0,0,0;
--color-app-divider: 0,0,0;
--color-app-selected: 0,0,0;
--color-app-hover: 0,0,0;
--color-app-separator: 0,0,0;
}

View file

@ -1,3 +1,5 @@
@import "./colors.scss";
@tailwind base;
@tailwind components;
@tailwind utilities;

View file

@ -2,11 +2,17 @@
const plugin = require('tailwindcss/plugin');
const defaultTheme = require('tailwindcss/defaultTheme');
function alpha(variableName) {
// some tailwind magic to allow us to specify opacity with CSS variables (eg: bg-app/80)
// https://tailwindcss.com/docs/customizing-colors#using-css-variables
return `rgba(var(${variableName}), <alpha-value>)`;
}
module.exports = function (app, options) {
let config = {
content: [
!options?.ignorePackages && '../../packages/*/src/**/*.{js,ts,jsx,tsx,html}',
app ? `../../apps/${app}/src/**/*.{js,ts,jsx,tsx,html}` : `./src/**/*.{js,ts,jsx,tsx,html}`
!options?.ignorePackages && '../../packages/*/src/**/*.{ts,tsx,html}',
app ? `../../apps/${app}/src/**/*.{ts,tsx,html}` : `./src/**/*.{ts,tsx,html}`
],
darkMode: app == 'landing' ? 'class' : 'media',
mode: 'jit',
@ -15,13 +21,6 @@ module.exports = function (app, options) {
xs: '475px',
...defaultTheme.screens
},
// fontFamily: {
// sans: ['Inter', 'ui-sans-serif', 'system-ui'],
// serif: ['Inter', 'ui-serif', 'Georgia'],
// mono: ['ui-monospace', 'SFMono-Regular'],
// display: ['Inter'],
// body: ['"Inter"']
// },
fontSize: {
'tiny': '.65rem',
'xs': '.75rem',
@ -37,52 +36,38 @@ module.exports = function (app, options) {
'7xl': '5rem'
},
extend: {
boxShadow: {
box: '0px 4px 9px rgba(0, 0, 0, 0.05)',
backdrop: '0px 4px 66px rgba(0, 0, 0, 0.08)',
deep: '0px 4px 66px rgba(0, 0, 0, 0.68)'
},
bg: {
funky: 'linear-gradient(90.63deg,#46bcff 12.1%,#85edfb 50.85%,#e04cf8 91.09%)'
},
colors: {
primary: {
DEFAULT: '#2599FF',
50: '#FFFFFF',
100: '#F1F8FF',
200: '#BEE1FF',
300: '#8BC9FF',
400: '#58B1FF',
500: '#2599FF',
600: '#0081F1',
700: '#0065BE',
800: '#004A8B',
900: '#002F58'
accent: {
DEFAULT: alpha('--color-accent'),
faint: alpha('--color-accent-faint'),
deep: alpha('--color-accent-deep')
},
gray: {
DEFAULT: '#505468',
50: '#F1F1F4',
100: '#E8E9ED',
150: '#E0E1E6',
200: '#D8DAE3',
250: '#D2D4DC',
300: '#C0C2CE',
350: '#A6AABF',
400: '#9196A8',
450: '#71758A',
500: '#303544',
550: '#20222d',
600: '#171720',
650: '#121219',
700: '#121317',
750: '#0D0E11',
800: '#0C0C0F',
850: '#08090D',
900: '#060609',
950: '#030303'
ink: {
DEFAULT: alpha('--color-ink'),
dull: alpha('--color-ink-dull'),
faint: alpha('--color-ink-faint')
},
sidebar: {
DEFAULT: alpha('--color-sidebar'),
box: alpha('--color-sidebar-box'),
border: alpha('--color-sidebar-border'),
divider: alpha('--color-sidebar-divider'),
button: alpha('--color-sidebar-button'),
selected: alpha('--color-sidebar-selected'),
separator: alpha('--color-sidebar-separator')
},
app: {
DEFAULT: alpha('--color-app'),
box: alpha('--color-app-box'),
input: alpha('--color-app-input'),
border: alpha('--color-app-border'),
divider: alpha('--color-app-divider'),
button: alpha('--color-app-button'),
selected: alpha('--color-app-selected'),
separator: alpha('--color-app-separator'),
hover: alpha('--color-app-hover')
}
},
// fontFamily: { sans: ['Inter', ...defaultTheme.fontFamily.sans] }
extend: {
transitionTimingFunction: {
'css': 'ease',
@ -117,9 +102,6 @@ module.exports = function (app, options) {
}
}
},
variants: {
extend: {}
},
plugins: [
require('@tailwindcss/forms'),
plugin(({ addVariant }) => {
@ -135,3 +117,41 @@ module.exports = function (app, options) {
}
return config;
};
// primary: {
// DEFAULT: '#2599FF',
// 50: '#FFFFFF',
// 100: '#F1F8FF',
// 200: '#BEE1FF',
// 300: '#8BC9FF',
// 400: '#58B1FF',
// 500: '#2599FF',
// 600: '#0081F1',
// 700: '#0065BE',
// 800: '#004A8B',
// 900: '#002F58'
// },
// gray: {
// DEFAULT: '#505468',
// 50: '#F1F1F4',
// 100: '#E8E9ED',
// 150: '#E0E1E6',
// 200: '#D8DAE3',
// 250: '#D2D4DC',
// 300: '#C0C2CE',
// 350: '#A6AABF',
// 400: '#9196A8',
// 450: '#71758A',
// 500: '#303544',
// 550: '#20222d',
// 600: '#171720',
// 650: '#121219',
// 700: '#121317',
// 750: '#0D0E11',
// 800: '#0C0C0F',
// 850: '#08090D',
// 900: '#060609',
// 950: '#030303'
// }
// },
// fontFamily: { sans: ['Inter', ...defaultTheme.fontFamily.sans] }