Improve dark mode (#5819)

* Improve dark mode

- Switch from `slate` to `zinc` for the gray color palette
- Darken overall dark mode UI
- Switch from `green` to `emerald` for the green color palette
- Update a few previously missed instances of title case to sentence case
- Consolidate button styles and change naming from `bright` to `secondary`
- Update button disabled styles
- Fix tooltip not adjusting to content width
- Update graph tooltip layout and typography
- Add transition effects to hover states
- Reduce footer logo size

* Fix oversights

- Update funnel graph colors
- Update graph grid colors
- Improve focus styles
- Improve disabled input styles

* Fix more oversights in relation to dashboard filtering

- Improve consistency of input, button, combobox and modal components in relation to settings area
- Fix segment tooltip color

* Fix search input style in funnel and segments dropdowns

* Add white background to favicon images in dark mode

- The GitHub and ChatGPT favicons are hard to see in dark mode, so we add a white background to them.

* Fix tooltip color to fit all backgrounds in dark mode

* Fix tests

* Fixed more tests

* Extract SourceFavicon component to eliminate favicon duplication

* Fix regression on installation page after rebase

* Fix formatting issues

* Fix favicon test failure in CI by reading placeholder icon at compile time

* Undo previous commit
This commit is contained in:
Sanne de Vries 2025-10-28 03:28:15 -05:00 committed by GitHub
parent 46f05d81c9
commit 91363a2825
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
87 changed files with 472 additions and 403 deletions

View File

@ -4,7 +4,6 @@
@import './loader.css' layer(components); @import './loader.css' layer(components);
@import './tooltip.css' layer(components); @import './tooltip.css' layer(components);
@import './flatpickr-colors.css' layer(components); @import './flatpickr-colors.css' layer(components);
@import './chartjs.css' layer(components);
@plugin "@tailwindcss/forms"; @plugin "@tailwindcss/forms";
@ -28,6 +27,24 @@
[role='button']:not(:disabled) { [role='button']:not(:disabled) {
cursor: pointer; cursor: pointer;
} }
*:focus-visible {
@apply ring-2 ring-indigo-500 ring-offset-2 dark:ring-offset-gray-900 outline-none;
}
}
@layer components {
/* Replace Tailwind form plugin's focus with focus-visible */
[type='checkbox']:focus,
[type='radio']:focus {
outline: none;
box-shadow: none;
}
[type='checkbox']:focus-visible,
[type='radio']:focus-visible {
@apply ring-2 ring-indigo-500 ring-offset-2 outline-none;
}
} }
@theme { @theme {
@ -45,28 +62,40 @@
--color-yellow-800: var(--color-amber-800); --color-yellow-800: var(--color-amber-800);
--color-yellow-900: var(--color-amber-900); --color-yellow-900: var(--color-amber-900);
--color-yellow-950: var(--color-amber-950); --color-yellow-950: var(--color-amber-950);
--color-green-50: var(--color-emerald-50);
--color-green-100: var(--color-emerald-100);
--color-green-200: var(--color-emerald-200);
--color-green-300: var(--color-emerald-300);
--color-green-400: var(--color-emerald-400);
--color-green-500: var(--color-emerald-500);
--color-green-600: var(--color-emerald-600);
--color-green-700: var(--color-emerald-700);
--color-green-800: var(--color-emerald-800);
--color-green-900: var(--color-emerald-900);
--color-green-950: var(--color-emerald-950);
/* gray: colors.slate - Map gray to slate colors */ /* gray: colors.slate - Map gray to slate colors */
--color-gray-50: var(--color-slate-50); --color-gray-50: var(--color-zinc-50);
--color-gray-100: var(--color-slate-100); --color-gray-100: var(--color-zinc-100);
--color-gray-200: var(--color-slate-200); --color-gray-200: var(--color-zinc-200);
--color-gray-300: var(--color-slate-300); --color-gray-300: var(--color-zinc-300);
--color-gray-400: var(--color-slate-400); --color-gray-400: var(--color-zinc-400);
--color-gray-500: var(--color-slate-500); --color-gray-500: var(--color-zinc-500);
--color-gray-600: var(--color-slate-600); --color-gray-600: var(--color-zinc-600);
--color-gray-700: var(--color-slate-700); --color-gray-700: var(--color-zinc-700);
--color-gray-800: var(--color-slate-800); --color-gray-800: var(--color-zinc-800);
--color-gray-900: var(--color-slate-900); --color-gray-900: var(--color-zinc-900);
--color-gray-950: var(--color-zinc-950);
/* Custom gray shades from config (override some slate values) */ /* Custom gray shades from config (override some slate values) */
--color-gray-150: rgb(234 238 244); --color-gray-150: rgb(236 236 238);
--color-gray-825: rgb(37 47 63); --color-gray-750: rgb(50 50 54);
--color-gray-850: rgb(26 32 44); --color-gray-825: rgb(35 35 38);
--color-gray-950: rgb(13 18 30); --color-gray-850: rgb(34 34 38);
/* Set v3 default ring behavior */ /* Set v3 default ring behavior */
--default-ring-width: 3px; --default-ring-width: 2px;
--default-ring-color: var(--color-blue-500); --default-ring-color: var(--color-indigo-500);
} }
@media print { @media print {
@ -91,7 +120,7 @@
} }
.button { .button {
@apply inline-flex justify-center px-3.5 py-2 text-sm font-medium text-white bg-indigo-600 border border-transparent rounded-md leading-5 transition hover:bg-indigo-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500; @apply inline-flex justify-center px-3.5 py-2 text-sm font-medium text-white bg-indigo-600 border border-transparent rounded-md leading-5 transition hover:bg-indigo-700;
} }
.button[disabled] { .button[disabled] {
@ -264,15 +293,15 @@ blockquote {
} }
.table-striped tbody tr:nth-child(odd) { .table-striped tbody tr:nth-child(odd) {
background-color: #f1f5f8; background-color: var(--color-gray-100);
} }
.dark .table-striped tbody tr:nth-child(odd) { .dark .table-striped tbody tr:nth-child(odd) {
background-color: rgb(37 47 63); background-color: var(--color-gray-800);
} }
.dark .table-striped tbody tr:nth-child(even) { .dark .table-striped tbody tr:nth-child(even) {
background-color: rgb(26 32 44); background-color: var(--color-gray-900);
} }
.fade-enter { .fade-enter {
@ -298,10 +327,6 @@ blockquote {
background-color: inherit; background-color: inherit;
} }
.dark .fullwidth-shadow::before {
box-shadow: 0 4px 2px -2px rgb(200 200 200 / 10%);
}
iframe[hidden] { iframe[hidden] {
display: none; display: none;
} }
@ -316,10 +341,6 @@ iframe[hidden] {
.date-option-group { } .date-option-group { }
/* stylelint-enable */ /* stylelint-enable */
.popper-tooltip {
background-color: rgba(25 30 56);
}
.tooltip-arrow, .tooltip-arrow,
.tooltip-arrow::before { .tooltip-arrow::before {
position: absolute; position: absolute;

View File

@ -1,10 +0,0 @@
.chartjs-tooltip {
background-color: rgb(25 30 56);
position: absolute;
font-size: 14px;
font-style: normal;
padding: 10px 12px;
pointer-events: none;
border-radius: 5px;
z-index: 100;
}

View File

@ -12,10 +12,9 @@ import { useMountedEffect, useDebounce } from '../custom-hooks'
function Option({ isHighlighted, onClick, onMouseEnter, text, id }) { function Option({ isHighlighted, onClick, onMouseEnter, text, id }) {
const className = classNames( const className = classNames(
'relative cursor-pointer select-none py-2 px-3', 'relative cursor-pointer select-none py-2 px-3 text-gray-900 dark:text-gray-300',
{ {
'text-gray-900 dark:text-gray-300': !isHighlighted, 'bg-gray-100 dark:bg-gray-700': isHighlighted
'bg-indigo-600 text-white': isHighlighted
} }
) )
@ -340,9 +339,10 @@ export default function PlausibleCombobox({
} }
const defaultBoxClass = const defaultBoxClass =
'pl-2 pr-8 py-1 w-full dark:bg-gray-900 dark:text-gray-300 rounded-md shadow-xs border border-gray-300 dark:border-gray-700 focus-within:border-indigo-500 focus-within:ring-1 focus-within:ring-indigo-500' 'pl-2 pr-8 py-1 w-full dark:bg-gray-750 dark:text-gray-300 rounded-md shadow-xs border border-gray-300 dark:border-gray-750'
const finalBoxClass = classNames(boxClass || defaultBoxClass, { const finalBoxClass = classNames(boxClass || defaultBoxClass, {
'border-indigo-500 ring-1 ring-indigo-500': isOpen 'ring-3 ring-indigo-500/20 dark:ring-indigo-500/25 border !border-indigo-500':
isOpen
}) })
return ( return (
@ -365,7 +365,7 @@ export default function PlausibleCombobox({
> >
<ul <ul
ref={listRef} ref={listRef}
className="z-50 absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1/5 ring-black focus:outline-hidden sm:text-sm dark:bg-gray-900" className="z-50 absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1/5 ring-black focus:outline-hidden sm:text-sm dark:bg-gray-800"
> >
{renderDropDownContent()} {renderDropDownContent()}
</ul> </ul>

View File

@ -28,7 +28,7 @@ export default function FilterOperatorSelector(props) {
<BlurMenuButtonOnEscape targetRef={buttonRef} /> <BlurMenuButtonOnEscape targetRef={buttonRef} />
<Popover.Button <Popover.Button
ref={buttonRef} ref={buttonRef}
className="relative flex justify-between items-center w-full rounded-md border border-gray-300 dark:border-gray-500 shadow-xs px-4 py-2 bg-white dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-850 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 dark:focus:ring-offset-gray-900 focus:ring-indigo-500 text-left" className="relative flex justify-between items-center w-full rounded-md border border-gray-300 dark:border-gray-750 px-4 py-2 bg-white dark:bg-gray-750 text-sm text-gray-700 dark:text-gray-200 dark:hover:bg-gray-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 dark:focus:ring-offset-gray-900 focus:ring-indigo-500 text-left"
> >
{FILTER_OPERATIONS_DISPLAY_NAMES[props.selectedType]} {FILTER_OPERATIONS_DISPLAY_NAMES[props.selectedType]}
<ChevronDownIcon <ChevronDownIcon

View File

@ -35,17 +35,18 @@ export function FeatureSetupNotice({
function renderCallToAction() { function renderCallToAction() {
return ( return (
<a href={callToAction.link} className="ml-2 sm:ml-4 button px-2 sm:px-4"> <a
<p className="flex flex-col justify-center text-xs sm:text-sm"> href={callToAction.link}
{callToAction.action} className="flex items-center gap-x-1.5 ml-2 sm:ml-4 button px-2 sm:px-4"
</p> >
<p className="text-xs sm:text-sm font-medium">{callToAction.action}</p>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
strokeWidth={1.5} strokeWidth={1.5}
stroke="currentColor" stroke="currentColor"
className="ml-2 w-5 h-5" className="size-4"
> >
<path <path
strokeLinecap="round" strokeLinecap="round"
@ -61,7 +62,7 @@ export function FeatureSetupNotice({
return ( return (
<button <button
onClick={requestHideSection} onClick={requestHideSection}
className="inline-block px-2 sm:px-4 py-2 border border-gray-300 dark:border-gray-500 leading-5 rounded-md text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition ease-in-out duration-150" className="inline-block px-2 sm:px-4 py-2 font-medium leading-5 rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-700 text-gray-800 dark:text-gray-100 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-white hover:shadow-sm transition-all duration-150"
> >
Hide this report Hide this report
</button> </button>
@ -71,11 +72,11 @@ export function FeatureSetupNotice({
return ( return (
<div className="sm:mx-32 mt-6 mb-3"> <div className="sm:mx-32 mt-6 mb-3">
<div className="py-3"> <div className="py-3">
<div className="text-center mt-2 text-gray-800 dark:text-gray-200"> <div className="text-center text-pretty mt-2 text-gray-800 dark:text-gray-200 font-medium">
{title} {title}
</div> </div>
<div className="text-justify mt-4 font-small text-sm text-gray-500 dark:text-gray-200"> <div className="text-center text-pretty mt-4 font-small text-sm text-gray-500 dark:text-gray-200">
{info} {info}
</div> </div>

View File

@ -32,14 +32,15 @@ const panel = {
const toggleButton = { const toggleButton = {
classNames: { classNames: {
rounded: 'flex items-center rounded text-sm leading-tight h-9', rounded:
'flex items-center rounded text-sm leading-tight h-9 transition-all duration-150',
shadow: shadow:
'bg-white dark:bg-gray-800 shadow-sm text-gray-800 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-900', 'bg-white dark:bg-gray-750 shadow-sm text-gray-800 dark:text-gray-200 dark:hover:bg-gray-700',
ghost: ghost:
'text-gray-700 dark:text-gray-100 hover:bg-gray-200 dark:hover:bg-gray-900', 'text-gray-700 dark:text-gray-100 hover:bg-gray-200 dark:hover:bg-gray-900',
truncatedText: 'truncate block font-medium', truncatedText: 'truncate block font-medium',
linkLike: linkLike:
'text-gray-700 dark:text-gray-300 hover:text-indigo-600 dark:hover:text-indigo-600' 'text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 transition-colors duration-150'
} }
} }
@ -54,12 +55,12 @@ const items = {
hoverLink: classNames( hoverLink: classNames(
'hover:bg-gray-100', 'hover:bg-gray-100',
'hover:text-gray-900', 'hover:text-gray-900',
'dark:hover:bg-gray-900', 'dark:hover:bg-gray-700',
'dark:hover:text-gray-100', 'dark:hover:text-gray-100',
'focus-within:bg-gray-100', 'focus-within:bg-gray-100',
'focus-within:text-gray-900', 'focus-within:text-gray-900',
'dark:focus-within:bg-gray-900', 'dark:focus-within:bg-gray-700',
'dark:focus-within:text-gray-100' 'dark:focus-within:text-gray-100'
), ),
roundedStart: 'first-of-type:rounded-t-md', roundedStart: 'first-of-type:rounded-t-md',

View File

@ -66,7 +66,7 @@ export const SearchInput = ({
type="text" type="text"
placeholder={isFocused ? placeholderFocused : placeholderUnfocused} placeholder={isFocused ? placeholderFocused : placeholderUnfocused}
className={classNames( className={classNames(
'shadow-sm dark:bg-gray-900 dark:text-gray-100 focus:ring-indigo-500 focus:border-indigo-500 block border-gray-300 dark:border-gray-500 rounded-md dark:bg-gray-800 w-48', 'dark:text-gray-100 block border-gray-300 dark:border-gray-750 rounded-md dark:bg-gray-750 w-48 dark:placeholder:text-gray-400 focus:outline-none focus:ring-3 focus:ring-indigo-500/20 dark:focus:ring-indigo-500/25 focus:border-indigo-500',
className className
)} )}
onChange={debouncedOnSearchInputChange} onChange={debouncedOnSearchInputChange}

View File

@ -32,9 +32,10 @@ const TabButtonText = ({
active: boolean active: boolean
}) => ( }) => (
<span <span
className={classNames('truncate text-left', { className={classNames('truncate text-left transition-colors duration-150', {
'hover:text-indigo-600 cursor-pointer': !active, 'hover:text-indigo-700 dark:hover:text-indigo-400 cursor-pointer':
'text-indigo-700 dark:text-indigo-500 font-bold underline decoration-2 decoration-indigo-700 dark:decoration-indigo-500': !active,
'text-indigo-600 dark:text-indigo-500 font-bold underline decoration-2 decoration-indigo-600 dark:decoration-indigo-500':
active active
})} })}
> >

View File

@ -97,11 +97,11 @@ export default function Funnel({ funnelName, tabs }) {
const getPalette = () => { const getPalette = () => {
if (isDarkMode()) { if (isDarkMode()) {
return { return {
dataLabelBackground: 'rgba(25, 30, 56, 0.97)', dataLabelBackground: 'rgb(9, 9, 11)',
dataLabelTextColor: 'rgb(243, 244, 246)', dataLabelTextColor: 'rgb(244, 244, 245)',
visitorsBackground: 'rgb(99, 102, 241)', visitorsBackground: 'rgb(99, 102, 241)',
dropoffBackground: '#2F3949', dropoffBackground: 'rgb(63, 63, 70)',
dropoffStripes: 'rgb(25, 30, 56)', dropoffStripes: 'rgb(9, 9, 11)',
stepNameLegendColor: 'rgb(228, 228, 231)', stepNameLegendColor: 'rgb(228, 228, 231)',
visitorsLegendClass: 'bg-indigo-500', visitorsLegendClass: 'bg-indigo-500',
dropoffLegendClass: 'bg-gray-600', dropoffLegendClass: 'bg-gray-600',
@ -109,12 +109,12 @@ export default function Funnel({ funnelName, tabs }) {
} }
} else { } else {
return { return {
dataLabelBackground: 'rgba(25, 30, 56, 0.97)', dataLabelBackground: 'rgb(39, 39, 42)',
dataLabelTextColor: 'rgb(243, 244, 246)', dataLabelTextColor: 'rgb(244, 244, 245)',
visitorsBackground: 'rgb(99, 102, 241)', visitorsBackground: 'rgb(99, 102, 241)',
dropoffBackground: 'rgb(224, 231, 255)', dropoffBackground: 'rgb(224, 231, 255)',
dropoffStripes: 'rgb(255, 255, 255)', dropoffStripes: 'rgb(255, 255, 255)',
stepNameLegendColor: 'rgb(12, 24, 39)', stepNameLegendColor: 'rgb(24, 24, 27)',
visitorsLegendClass: 'bg-indigo-500', visitorsLegendClass: 'bg-indigo-500',
dropoffLegendClass: 'bg-indigo-100', dropoffLegendClass: 'bg-indigo-100',
smallBarClass: 'bg-indigo-300' smallBarClass: 'bg-indigo-300'

View File

@ -17,7 +17,7 @@ function DashboardStats({
updateImportedDataInView?: (v: boolean) => void updateImportedDataInView?: (v: boolean) => void
}) { }) {
const statsBoxClass = const statsBoxClass =
'relative min-h-[436px] w-full mt-5 p-4 flex flex-col bg-white dark:bg-gray-825 shadow-sm rounded-md md:min-h-initial md:h-27.25rem md:w-[calc(50%-10px)] md:ml-[10px] md:mr-[10px] first:ml-0 last:mr-0' 'relative min-h-[436px] w-full mt-5 p-4 flex flex-col bg-white dark:bg-gray-900 shadow-sm rounded-md md:min-h-initial md:h-27.25rem md:w-[calc(50%-10px)] md:ml-[10px] md:mr-[10px] first:ml-0 last:mr-0'
return ( return (
<> <>

View File

@ -145,7 +145,7 @@ export function KeybindHint({
return ( return (
<kbd <kbd
className={classNames( className={classNames(
'rounded border border-gray-200 dark:border-gray-600 px-2 font-mono font-normal text-xs text-gray-400', 'rounded border border-gray-200 dark:border-gray-600 px-1.5 font-medium text-xs text-gray-400',
className className
)} )}
> >

View File

@ -290,7 +290,7 @@ const SeeMoreMenu = ({
<div className="py-4 px-4"> <div className="py-4 px-4">
<AppliedFilterPillsList <AppliedFilterPillsList
direction="vertical" direction="vertical"
pillClassName="dark:!shadow-gray-950/60" pillClassName="!shadow-none !bg-gray-100 dark:!bg-gray-700"
slice={{ slice={{
type: 'no-render-outside', type: 'no-render-outside',
start: visibleFiltersCount start: visibleFiltersCount
@ -298,7 +298,7 @@ const SeeMoreMenu = ({
/> />
</div> </div>
{showSomeActions && ( {showSomeActions && (
<div className="mb-1 border-gray-200 dark:border-gray-500 border-b"></div> <div className="mb-1 border-gray-200 dark:border-gray-700 border-b"></div>
)} )}
</> </>
)} )}

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
export const MenuSeparator = () => ( export const MenuSeparator = () => (
<div className="my-1 border-gray-200 dark:border-gray-500 border-b" /> <div className="my-1 border-gray-200 dark:border-gray-700 border-b" />
) )

View File

@ -37,10 +37,21 @@ const ArrowKeybind = ({
) )
} }
function ArrowIcon({ direction }: { direction: 'left' | 'right' }) { function ArrowIcon({
direction,
disabled = false
}: {
direction: 'left' | 'right'
disabled?: boolean
}) {
return ( return (
<svg <svg
className="feather h-4 w-4" className={classNames(
'feather size-4',
disabled
? 'text-gray-400 dark:text-gray-600'
: 'text-gray-700 dark:text-gray-300'
)}
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="none" fill="none"
@ -74,14 +85,15 @@ export function MovePeriodArrows({ className }: { className?: string }) {
const canGoForward = const canGoForward =
getDateForShiftedPeriod({ site, query, direction: 1 }) !== null getDateForShiftedPeriod({ site, query, direction: 1 }) !== null
const sharedClass = 'flex items-center px-1 sm:px-2 dark:text-gray-100' const sharedClass =
const enabledClass = 'hover:bg-gray-100 dark:hover:bg-gray-900' 'flex items-center px-1 sm:px-2 dark:text-gray-100 transition-colors duration-150'
const disabledClass = 'bg-gray-300 dark:bg-gray-950 cursor-not-allowed' const enabledClass = 'hover:bg-gray-100 dark:hover:bg-gray-700'
const disabledClass = 'bg-gray-200 dark:bg-gray-850 cursor-not-allowed'
return ( return (
<div <div
className={classNames( className={classNames(
'flex rounded shadow bg-white mr-2 sm:mr-4 cursor-pointer focus:z-10 dark:bg-gray-800', 'flex rounded shadow bg-white mr-2 sm:mr-4 cursor-pointer focus:z-10 dark:bg-gray-750',
className className
)} )}
> >
@ -102,7 +114,7 @@ export function MovePeriodArrows({ className }: { className?: string }) {
: (search) => search : (search) => search
} }
> >
<ArrowIcon direction="left" /> <ArrowIcon direction="left" disabled={!canGoBack} />
</AppNavigationLink> </AppNavigationLink>
<AppNavigationLink <AppNavigationLink
className={classNames(sharedClass, 'rounded-r', { className={classNames(sharedClass, 'rounded-r', {
@ -120,7 +132,7 @@ export function MovePeriodArrows({ className }: { className?: string }) {
: (search) => search : (search) => search
} }
> >
<ArrowIcon direction="right" /> <ArrowIcon direction="right" disabled={!canGoForward} />
</AppNavigationLink> </AppNavigationLink>
{!!dashboardRouteMatch && <ArrowKeybind keyboardKey="ArrowLeft" />} {!!dashboardRouteMatch && <ArrowKeybind keyboardKey="ArrowLeft" />}
{!!dashboardRouteMatch && <ArrowKeybind keyboardKey="ArrowRight" />} {!!dashboardRouteMatch && <ArrowKeybind keyboardKey="ArrowRight" />}

View File

@ -33,7 +33,7 @@ function TopBarStickyWrapper({ children }: { children: ReactNode }) {
'relative top-0 py-2 sm:py-3 z-10', 'relative top-0 py-2 sm:py-3 z-10',
!site.embedded && !site.embedded &&
!inView && !inView &&
'sticky fullwidth-shadow bg-gray-50 dark:bg-gray-850' 'sticky fullwidth-shadow bg-gray-50 dark:bg-gray-950'
)} )}
> >
{children} {children}

View File

@ -42,13 +42,13 @@ interface SegmentModalProps {
const primaryNeutralButtonClassName = 'button !px-3' const primaryNeutralButtonClassName = 'button !px-3'
const primaryNegativeButtonClassName = classNames( const primaryNegativeButtonClassName = classNames(
'button !px-3', 'button !px-3.5',
'items-center !bg-red-500 dark:!bg-red-500 hover:!bg-red-600 dark:hover:!bg-red-700 whitespace-nowrap' 'items-center !bg-red-500 dark:!bg-red-500 hover:!bg-red-600 dark:hover:!bg-red-700 whitespace-nowrap'
) )
const secondaryButtonClassName = classNames( const secondaryButtonClassName = classNames(
'button !px-3', 'button !px-3.5',
'border !border-gray-300 dark:!border-gray-500 !text-gray-700 dark:!text-gray-300 !bg-transparent hover:!bg-gray-100 dark:hover:!bg-gray-850' 'border !border-gray-300 dark:!border-gray-700 !bg-white dark:!bg-gray-700 !text-gray-800 dark:!text-gray-100 hover:!text-gray-900 hover:!shadow-sm dark:hover:!bg-gray-600 dark:hover:!text-white'
) )
const SegmentActionModal = ({ const SegmentActionModal = ({
@ -200,7 +200,9 @@ export const DeleteSegmentModal = ({
} }
const FormTitle = ({ children }: { children?: ReactNode }) => ( const FormTitle = ({ children }: { children?: ReactNode }) => (
<h1 className="text-xl font-bold dark:text-gray-100 mb-2">{children}</h1> <h1 className="text-lg font-medium text-gray-900 dark:text-gray-100 leading-7 mb-8">
{children}
</h1>
) )
const ButtonsRow = ({ const ButtonsRow = ({
@ -210,7 +212,7 @@ const ButtonsRow = ({
className?: string className?: string
children?: ReactNode children?: ReactNode
}) => ( }) => (
<div className={classNames('mt-8 flex gap-x-4 items-center', className)}> <div className={classNames('mt-8 flex gap-x-3 items-center', className)}>
{children} {children}
</div> </div>
) )
@ -228,7 +230,7 @@ const SegmentNameInput = ({
<> <>
<label <label
htmlFor="name" htmlFor="name"
className="block text-md font-medium text-gray-700 dark:text-gray-300" className="block mb-1.5 text-sm font-medium dark:text-gray-100 text-gray-700 dark:text-gray-300"
> >
Segment name Segment name
</label> </label>
@ -238,7 +240,7 @@ const SegmentNameInput = ({
onChange={(e) => onChange(e.target.value)} onChange={(e) => onChange(e.target.value)}
placeholder={namePlaceholder} placeholder={namePlaceholder}
id="name" id="name"
className="block mt-2 p-2 w-full dark:bg-gray-900 dark:text-gray-300 rounded-md shadow-xs border border-gray-300 dark:border-gray-700 focus-within:border-indigo-500 focus-within:ring-1 focus-within:ring-indigo-500" className="block px-3.5 py-2.5 w-full text-sm dark:text-gray-300 rounded-md border border-gray-300 dark:border-gray-750 dark:bg-gray-750 focus:outline-none focus:ring-3 focus:ring-indigo-500/20 dark:focus:ring-indigo-500/25 focus:border-indigo-500"
/> />
</> </>
) )
@ -265,7 +267,7 @@ const SegmentTypeSelector = ({
] ]
return ( return (
<div className="mt-4 flex flex-col gap-y-4"> <div className="mt-6 flex flex-col gap-y-4">
{options.map(({ type, name, description }) => ( {options.map(({ type, name, description }) => (
<div key={type}> <div key={type}>
<div className="flex"> <div className="flex">
@ -275,14 +277,16 @@ const SegmentTypeSelector = ({
type="radio" type="radio"
value="" value=""
onChange={() => onChange(type)} onChange={() => onChange(type)}
className="mt-4 w-4 h-4 text-indigo-600 bg-gray-100 border-gray-300 focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:border-gray-600" className="mt-px size-4.5 cursor-pointer text-indigo-600 dark:bg-transparent border-gray-400 dark:border-gray-600 checked:border-indigo-600 dark:checked:border-white"
/> />
<label <label
htmlFor={`segment-type-${type}`} htmlFor={`segment-type-${type}`}
className="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300" className="block ml-3 text-sm font-medium dark:text-gray-100 flex flex-col flex-inline"
> >
<div className="font-bold">{name}</div> <div>{name}</div>
<div className="mt-1">{description}</div> <div className="text-gray-500 dark:text-gray-400 mb-2 text-sm">
{description}
</div>
</label> </label>
</div> </div>
</div> </div>
@ -531,7 +535,7 @@ export const SegmentModal = ({ id }: { id: SavedSegment['id'] }) => {
{data?.segment_data ? SEGMENT_TYPE_LABELS[data.type] : false} {data?.segment_data ? SEGMENT_TYPE_LABELS[data.type] : false}
</Placeholder> </Placeholder>
</div> </div>
<div className="my-4 border-b border-gray-300" /> <div className="my-4 border-b border-gray-300 dark:border-gray-700" />
{!!data?.segment_data && ( {!!data?.segment_data && (
<> <>
<FiltersInSegment segment_data={data.segment_data} /> <FiltersInSegment segment_data={data.segment_data} />

View File

@ -347,7 +347,7 @@ export default function Behaviours({ importedDataInView }) {
return ( return (
<div className="items-start justify-between block w-full mt-6 md:flex relative"> <div className="items-start justify-between block w-full mt-6 md:flex relative">
<div className="w-full p-4 bg-white rounded-md shadow-sm dark:bg-gray-825"> <div className="w-full p-4 bg-white rounded-md shadow-sm dark:bg-gray-900">
<div className="flex justify-between w-full"> <div className="flex justify-between w-full">
<div className="flex gap-x-1"> <div className="flex gap-x-1">
<h3 className="font-bold dark:text-gray-100"> <h3 className="font-bold dark:text-gray-100">

View File

@ -155,7 +155,7 @@ export default function Properties({ afterFetchData }) {
: '' : ''
const comboboxValues = propKey ? [{ value: propKey, label: propKey }] : [] const comboboxValues = propKey ? [{ value: propKey, label: propKey }] : []
const boxClass = classNames( const boxClass = classNames(
'pl-2 pr-8 py-1 bg-transparent dark:text-gray-300 rounded-md shadow-sm border border-gray-300 dark:border-gray-500', 'pl-2 pr-8 py-1 bg-transparent dark:text-gray-300 rounded-md shadow-sm border border-gray-300 dark:bg-gray-750 dark:border-gray-750',
{ {
'pointer-events-none': comboboxDisabled 'pointer-events-none': comboboxDisabled
} }

View File

@ -5,6 +5,11 @@ import { METRIC_LABELS, hasMultipleYears } from './graph-util'
import { MetricFormatterShort } from '../reports/metric-formatter' import { MetricFormatterShort } from '../reports/metric-formatter'
import { ChangeArrow } from '../reports/change-arrow' import { ChangeArrow } from '../reports/change-arrow'
// Function to detect if dark mode is active
const isDarkMode = () => {
return document.documentElement.classList.contains('dark')
}
const renderBucketLabel = function ( const renderBucketLabel = function (
query, query,
graphData, graphData,
@ -117,6 +122,9 @@ export default function GraphTooltip(graphData, metric, query) {
tooltipRoot = createRoot(tooltipEl) tooltipRoot = createRoot(tooltipEl)
} }
const bgClass = isDarkMode() ? 'bg-gray-950' : 'bg-gray-800'
tooltipEl.className = `absolute text-sm font-normal py-3 px-4 pointer-events-none rounded-md z-[100] min-w-[180px] ${bgClass}`
if (tooltipEl && offset && window.innerWidth < 768) { if (tooltipEl && offset && window.innerWidth < 768) {
tooltipEl.style.top = tooltipEl.style.top =
offset.y + offset.height + window.scrollY + 15 + 'px' offset.y + offset.height + window.scrollY + 15 + 'px'
@ -139,9 +147,9 @@ export default function GraphTooltip(graphData, metric, query) {
) )
tooltipRoot.render( tooltipRoot.render(
<aside className="text-gray-100 flex flex-col"> <aside className="text-gray-100 flex flex-col gap-1.5">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<span className="font-semibold mr-4 text-lg"> <span className="font-semibold mr-4 text-xs uppercase">
{METRIC_LABELS[metric]} {METRIC_LABELS[metric]}
</span> </span>
{tooltipData.comparisonDifference ? ( {tooltipData.comparisonDifference ? (
@ -156,26 +164,24 @@ export default function GraphTooltip(graphData, metric, query) {
{tooltipData.label ? ( {tooltipData.label ? (
<div className="flex flex-col"> <div className="flex flex-col">
<div className="flex flex-row justify-between items-center"> <div className="flex flex-row justify-between items-center text-sm">
<span className="flex items-center mr-4"> <span className="flex items-center mr-4">
<div <div
className="w-3 h-3 mr-1 rounded-full" className="size-2 mr-2 rounded-full"
style={{ backgroundColor: 'rgba(101,116,205)' }} style={{ backgroundColor: 'rgba(101,116,205)' }}
></div> ></div>
<span>{tooltipData.label}</span> <span>{tooltipData.label}</span>
</span> </span>
<span className="text-base font-bold"> <span className="font-bold">{tooltipData.formattedValue}</span>
{tooltipData.formattedValue}
</span>
</div> </div>
{tooltipData.comparisonLabel ? ( {tooltipData.comparisonLabel ? (
<div className="flex flex-row justify-between items-center"> <div className="flex flex-row justify-between items-center text-sm">
<span className="flex items-center mr-4"> <span className="flex items-center mr-4">
<div className="w-3 h-3 mr-1 rounded-full bg-gray-500"></div> <div className="size-2 mr-2 rounded-full bg-gray-500"></div>
<span>{tooltipData.comparisonLabel}</span> <span>{tooltipData.comparisonLabel}</span>
</span> </span>
<span className="text-base font-bold"> <span className="font-bold">
{tooltipData.formattedComparisonValue} {tooltipData.formattedComparisonValue}
</span> </span>
</div> </div>
@ -183,12 +189,14 @@ export default function GraphTooltip(graphData, metric, query) {
</div> </div>
) : null} ) : null}
{graphData.interval === 'month' ? ( {['month', 'day'].includes(graphData.interval) && (
<span className="font-semibold italic">Click to view month</span> <>
) : null} <hr className="border-gray-600 dark:border-gray-800 my-1" />
{graphData.interval === 'day' ? ( <span className="text-gray-300 dark:text-gray-400 text-xs">
<span className="font-semibold italic">Click to view day</span> Click to view {graphData.interval}
) : null} </span>
</>
)}
</aside> </aside>
) )
} }

View File

@ -84,11 +84,14 @@ class LineGraph extends React.Component {
suggestedMax: calculateMaximumY(dataSet), suggestedMax: calculateMaximumY(dataSet),
ticks: { ticks: {
callback: MetricFormatterShort[metric], callback: MetricFormatterShort[metric],
color: this.props.darkTheme ? 'rgb(243, 244, 246)' : undefined color: this.props.darkTheme ? 'rgb(161, 161, 170)' : undefined
}, },
grid: { grid: {
zeroLineColor: 'transparent', zeroLineColor: 'transparent',
drawBorder: false drawBorder: false,
color: this.props.darkTheme
? 'rgba(39, 39, 42, 0.75)'
: 'rgb(236, 236, 238)'
} }
}, },
yComparison: { yComparison: {
@ -144,7 +147,7 @@ class LineGraph extends React.Component {
shouldShowYear shouldShowYear
})(this.getLabelForValue(val)) })(this.getLabelForValue(val))
}, },
color: this.props.darkTheme ? 'rgb(243, 244, 246)' : undefined color: this.props.darkTheme ? 'rgb(161, 161, 170)' : undefined
} }
} }
}, },

View File

@ -128,7 +128,7 @@ export default function TopStats({
const statDisplayNameClass = classNames( const statDisplayNameClass = classNames(
'text-xs font-bold tracking-wide text-gray-500 uppercase dark:text-gray-400 whitespace-nowrap flex w-fit border-b', 'text-xs font-bold tracking-wide text-gray-500 uppercase dark:text-gray-400 whitespace-nowrap flex w-fit border-b',
{ {
'text-indigo-700 dark:text-indigo-500 border-indigo-700 dark:border-indigo-500': 'text-indigo-600 dark:text-indigo-500 border-indigo-600 dark:border-indigo-500':
isSelected, isSelected,
'group-hover:text-indigo-700 dark:group-hover:text-indigo-500 border-transparent': 'group-hover:text-indigo-700 dark:group-hover:text-indigo-500 border-transparent':
!isSelected !isSelected
@ -153,7 +153,7 @@ export default function TopStats({
'px-4 md:px-6 w-1/2 my-4 lg:w-auto group select-none', 'px-4 md:px-6 w-1/2 my-4 lg:w-auto group select-none',
{ {
'cursor-pointer': canMetricBeGraphed(stat), 'cursor-pointer': canMetricBeGraphed(stat),
'lg:border-l border-gray-300': index > 0, 'lg:border-l border-gray-300 dark:border-gray-700': index > 0,
'border-r lg:border-r-0': index % 2 === 0 'border-r lg:border-r-0': index % 2 === 0
} }
) )

View File

@ -168,7 +168,7 @@ export default function VisitorGraph({ updateImportedDataInView }) {
return ( return (
<div <div
className={ className={
'relative w-full mt-2 bg-white rounded-md shadow dark:bg-gray-825' 'relative w-full mt-2 bg-white rounded-md shadow dark:bg-gray-900'
} }
> >
{(topStatsLoading || graphLoading) && renderLoader()} {(topStatsLoading || graphLoading) && renderLoader()}

View File

@ -195,7 +195,7 @@ const WorldMap = ({
const colorScales = { const colorScales = {
[UIMode.dark]: ['#2e3954', '#6366f1'], [UIMode.dark]: ['#2e3954', '#6366f1'],
[UIMode.light]: ['#f3ebff', '#a779e9'] [UIMode.light]: ['#f5f3ff', '#a78bfa']
} }
const sharedCountryClass = classNames('transition-colors') const sharedCountryClass = classNames('transition-colors')
@ -203,19 +203,19 @@ const sharedCountryClass = classNames('transition-colors')
const countryClass = classNames( const countryClass = classNames(
sharedCountryClass, sharedCountryClass,
'stroke-1', 'stroke-1',
'fill-[#f8fafc]', 'fill-[#fafafa]',
'stroke-[#dae1e7]', 'stroke-[#dae1e7]',
'dark:fill-[#2d3747]', 'dark:fill-[#323236]',
'dark:stroke-[#1f2937]' 'dark:stroke-[#18181b]'
) )
const highlightedCountryClass = classNames( const highlightedCountryClass = classNames(
sharedCountryClass, sharedCountryClass,
'stroke-2', 'stroke-2',
'fill-[#f5f5f5]', 'fill-[#f4f4f5]',
'stroke-[#a779e9]', 'stroke-[#a78bfa]',
'dark:fill-[#374151]', 'dark:fill-[#3f3f46]',
'dark:stroke-[#4f46e5]' 'dark:stroke-[#6366f1]'
) )
/** /**

View File

@ -54,7 +54,7 @@ export const BreakdownTable = <TListItem extends { name: string }>({
/> />
)} )}
</div> </div>
<div className="my-4 border-b border-gray-300"></div> <div className="my-4 border-b border-gray-300 dark:border-gray-700"></div>
<div style={{ minHeight: `${MIN_HEIGHT_PX}px` }}> <div style={{ minHeight: `${MIN_HEIGHT_PX}px` }}>
{displayError && status === 'error' && <ErrorMessage error={error} />} {displayError && status === 'error' && <ErrorMessage error={error} />}
{isPending && <InitialLoadingSpinner />} {isPending && <InitialLoadingSpinner />}

View File

@ -174,7 +174,7 @@ class FilterModal extends React.Component {
Filter by {formatFilterGroup(this.props.modalType)} Filter by {formatFilterGroup(this.props.modalType)}
</h1> </h1>
<div className="mt-4 border-b border-gray-300"></div> <div className="mt-4 border-b border-gray-300 dark:border-gray-700"></div>
<main className="modal__content"> <main className="modal__content">
<form <form
className="flex flex-col" className="flex flex-col"

View File

@ -81,7 +81,7 @@ class Modal extends React.Component {
<button className="modal__close"></button> <button className="modal__close"></button>
<div <div
ref={this.node} ref={this.node}
className="modal__container dark:bg-gray-800 focus:outline-hidden" className="modal__container dark:bg-gray-900 focus:outline-hidden"
style={this.getStyle()} style={this.getStyle()}
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex={0} tabIndex={0}

View File

@ -13,6 +13,7 @@ import { addFilter } from '../../query'
import { useQueryContext } from '../../query-context' import { useQueryContext } from '../../query-context'
import { useSiteContext } from '../../site-context' import { useSiteContext } from '../../site-context'
import { SortDirection } from '../../hooks/use-order-by' import { SortDirection } from '../../hooks/use-order-by'
import { SourceFavicon } from '../sources/source-favicon'
function ReferrerDrilldownModal() { function ReferrerDrilldownModal() {
const { referrer } = useParams() const { referrer } = useParams()
@ -82,10 +83,9 @@ function ReferrerDrilldownModal() {
const renderIcon = useCallback((listItem) => { const renderIcon = useCallback((listItem) => {
return ( return (
<img <SourceFavicon
alt="" name={listItem.name}
src={`/favicon/sources/${encodeURIComponent(listItem.name)}`} className="size-4 mr-2 align-middle inline"
className="h-4 w-4 mr-2 align-middle inline"
/> />
) )
}, []) }, [])

View File

@ -11,6 +11,7 @@ import { addFilter } from '../../query'
import { useQueryContext } from '../../query-context' import { useQueryContext } from '../../query-context'
import { useSiteContext } from '../../site-context' import { useSiteContext } from '../../site-context'
import { SortDirection } from '../../hooks/use-order-by' import { SortDirection } from '../../hooks/use-order-by'
import { SourceFavicon } from '../sources/source-favicon'
const VIEWS = { const VIEWS = {
sources: { sources: {
@ -23,10 +24,9 @@ const VIEWS = {
}, },
renderIcon: (listItem) => { renderIcon: (listItem) => {
return ( return (
<img <SourceFavicon
alt="" name={listItem.name}
src={`/favicon/sources/${encodeURIComponent(listItem.name)}`} className="size-4 mr-2 align-middle inline"
className="h-4 w-4 mr-2 align-middle inline"
/> />
) )
} }

View File

@ -25,7 +25,7 @@ export default function MoreLink({ linkProps, list, className, onClick }) {
<div className={`w-full text-center ${className ? className : ''}`}> <div className={`w-full text-center ${className ? className : ''}`}>
<AppNavigationLink <AppNavigationLink
{...linkProps} {...linkProps}
className="leading-snug font-bold text-sm text-gray-500 dark:text-gray-400 hover:text-red-500 dark:hover:text-red-400 transition tracking-wide" className="leading-snug font-bold text-sm text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors duration-150 tracking-wide"
onClick={onClick} onClick={onClick}
> >
{detailsIcon()} {detailsIcon()}

View File

@ -8,6 +8,7 @@ import ImportedQueryUnsupportedWarning from '../../stats/imported-query-unsuppor
import { useQueryContext } from '../../query-context' import { useQueryContext } from '../../query-context'
import { useSiteContext } from '../../site-context' import { useSiteContext } from '../../site-context'
import { referrersDrilldownRoute } from '../../router' import { referrersDrilldownRoute } from '../../router'
import { SourceFavicon } from './source-favicon'
const NO_REFERRER = 'Direct / None' const NO_REFERRER = 'Direct / None'
@ -52,11 +53,9 @@ export default function Referrers({ source }) {
function renderIcon(listItem) { function renderIcon(listItem) {
return ( return (
<img <SourceFavicon
alt="" name={listItem.name}
src={`/favicon/sources/${encodeURIComponent(listItem.name)}`} className="inline size-4 mr-2 -mt-px align-middle"
referrerPolicy="no-referrer"
className="inline w-4 h-4 mr-2 -mt-px align-middle"
/> />
) )
} }

View File

@ -0,0 +1,26 @@
import React from 'react'
import classNames from 'classnames'
interface SourceFaviconProps {
name: string
className?: string
}
export const SourceFavicon = ({ name, className }: SourceFaviconProps) => {
const sourceName = name.toLowerCase()
const needsWhiteBg =
sourceName.includes('github') || sourceName.includes('chatgpt.com')
return (
<img
alt=""
src={`/favicon/sources/${encodeURIComponent(name)}`}
referrerPolicy="no-referrer"
className={classNames(
className,
needsWhiteBg &&
'dark:bg-white dark:border dark:border-white dark:rounded-full'
)}
/>
)
}

View File

@ -13,6 +13,7 @@ import {
import ImportedQueryUnsupportedWarning from '../imported-query-unsupported-warning' import ImportedQueryUnsupportedWarning from '../imported-query-unsupported-warning'
import { useQueryContext } from '../../query-context' import { useQueryContext } from '../../query-context'
import { useSiteContext } from '../../site-context' import { useSiteContext } from '../../site-context'
import { SourceFavicon } from './source-favicon'
import { import {
sourcesRoute, sourcesRoute,
channelsRoute, channelsRoute,
@ -63,13 +64,7 @@ function AllSources({ afterFetchData }) {
} }
function renderIcon(listItem) { function renderIcon(listItem) {
return ( return <SourceFavicon name={listItem.name} className="size-4 mr-2" />
<img
alt=""
src={`/favicon/sources/${encodeURIComponent(listItem.name)}`}
className="w-4 h-4 mr-2"
/>
)
} }
function chooseMetrics() { function chooseMetrics() {

View File

@ -99,7 +99,7 @@ function TooltipMessage({
ref={setPopperElement} ref={setPopperElement}
style={popperStyle} style={popperStyle}
{...popperAttributes} {...popperAttributes}
className="z-50 p-2 rounded-sm text-sm text-gray-100 font-bold popper-tooltip" className="z-50 p-2 rounded-sm text-sm text-gray-100 font-bold bg-gray-800 dark:bg-gray-700"
role="tooltip" role="tooltip"
> >
{children} {children}

View File

@ -74,7 +74,7 @@ defmodule PlausibleWeb.CustomerSupport.Team.Components.Audit do
class="float-right pt-4 text-sm" class="float-right pt-4 text-sm"
> >
&larr; Return &larr; Return
<kbd class="rounded border border-gray-200 dark:border-gray-600 px-2 font-mono font-normal text-xs text-gray-400"> <kbd class="rounded border border-gray-200 dark:border-gray-600 px-1.5 font-medium text-xs text-gray-400">
ESC ESC
</kbd> </kbd>
</.styled_link> </.styled_link>
@ -131,7 +131,7 @@ defmodule PlausibleWeb.CustomerSupport.Team.Components.Audit do
phx-value-before={@audit_page.metadata.before} phx-value-before={@audit_page.metadata.before}
phx-value-limit={@current_limit} phx-value-limit={@current_limit}
phx-target={@myself} phx-target={@myself}
theme="bright" theme="secondary"
> >
&larr; Prev &larr; Prev
</.button> </.button>
@ -143,7 +143,7 @@ defmodule PlausibleWeb.CustomerSupport.Team.Components.Audit do
phx-value-after={@audit_page.metadata.after} phx-value-after={@audit_page.metadata.after}
phx-value-limit={@current_limit} phx-value-limit={@current_limit}
phx-target={@myself} phx-target={@myself}
theme="bright" theme="secondary"
> >
Next &rarr; Next &rarr;
</.button> </.button>

View File

@ -237,7 +237,7 @@ defmodule PlausibleWeb.CustomerSupport.Team.Components.Billing do
value={@cost_estimate} value={@cost_estimate}
/> />
<.button theme="bright" phx-click="hide-plan-form" phx-target={@myself}> <.button theme="secondary" phx-click="hide-plan-form" phx-target={@myself}>
Cancel Cancel
</.button> </.button>

View File

@ -64,10 +64,10 @@ defmodule PlausibleWeb.Live.FunnelSettings.Form do
phx-target="#funnel-form" phx-target="#funnel-form"
phx-click-away="cancel-add-funnel" phx-click-away="cancel-add-funnel"
onkeydown="return event.key != 'Enter';" onkeydown="return event.key != 'Enter';"
class="bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8" class="bg-white dark:bg-gray-900 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"
> >
<.title class="mb-6"> <.title class="mb-6">
{if @funnel, do: "Edit", else: "Add"} Funnel {if @funnel, do: "Edit", else: "Add"} funnel
</.title> </.title>
<.input <.input
@ -76,12 +76,12 @@ defmodule PlausibleWeb.Live.FunnelSettings.Form do
autocomplete="off" autocomplete="off"
placeholder="e.g. From Blog to Purchase" placeholder="e.g. From Blog to Purchase"
autofocus autofocus
label="Funnel Name" label="Funnel name"
/> />
<div id="steps-builder" class="mt-6"> <div id="steps-builder" class="mt-6">
<.label> <.label>
Funnel Steps Funnel steps
</.label> </.label>
<div :for={step_idx <- @step_ids} class="flex mb-3 mt-3"> <div :for={step_idx <- @step_ids} class="flex mb-3 mt-3">
@ -139,7 +139,7 @@ defmodule PlausibleWeb.Live.FunnelSettings.Form do
length(@step_ids) > map_size(@selections_made) length(@step_ids) > map_size(@selections_made)
} }
> >
<span>{if @funnel, do: "Update", else: "Add"} Funnel</span> <span>{if @funnel, do: "Update", else: "Add"} funnel</span>
</.button> </.button>
</div> </div>
</.form> </.form>

View File

@ -14,7 +14,7 @@ defmodule PlausibleWeb.Live.FunnelSettings.List do
<div> <div>
<.filter_bar filter_text={@filter_text} placeholder="Search Funnels"> <.filter_bar filter_text={@filter_text} placeholder="Search Funnels">
<.button id="add-funnel-button" phx-click="add-funnel" mt?={false}> <.button id="add-funnel-button" phx-click="add-funnel" mt?={false}>
Add Funnel Add funnel
</.button> </.button>
</.filter_bar> </.filter_bar>

View File

@ -178,7 +178,7 @@ defmodule PlausibleWeb.Components.Billing do
def usage_and_limits_table(assigns) do def usage_and_limits_table(assigns) do
~H""" ~H"""
<table class="min-w-full text-gray-900 dark:text-gray-100" {@rest}> <table class="min-w-full text-gray-900 dark:text-gray-100" {@rest}>
<tbody class="divide-y divide-gray-200 dark:divide-gray-600"> <tbody class="divide-y divide-gray-200 dark:divide-gray-700">
{render_slot(@inner_block)} {render_slot(@inner_block)}
</tbody> </tbody>
</table> </table>
@ -209,7 +209,7 @@ defmodule PlausibleWeb.Components.Billing do
~H""" ~H"""
<div <div
id="monthly-quota-box" id="monthly-quota-box"
class="w-full flex-1 h-32 px-2 py-4 text-center bg-gray-100 rounded-sm dark:bg-gray-900 w-max-md" class="w-full flex-1 h-32 px-2 py-4 text-center bg-gray-100 rounded-sm dark:bg-gray-800 w-max-md"
> >
<h4 class="font-black dark:text-gray-100">Monthly quota</h4> <h4 class="font-black dark:text-gray-100">Monthly quota</h4>
<div class="py-2 text-xl font-medium dark:text-gray-100"> <div class="py-2 text-xl font-medium dark:text-gray-100">
@ -276,7 +276,7 @@ defmodule PlausibleWeb.Components.Billing do
id={@id} id={@id}
onclick={"if (#{@confirmed}) {#{@js_action_expr}}"} onclick={"if (#{@confirmed}) {#{@js_action_expr}}"}
class={[ class={[
"text-sm w-full mt-6 block rounded-md py-2 px-3 text-center font-semibold leading-6 text-white", "text-sm w-full mt-6 block rounded-md py-2 px-3 text-center font-semibold leading-6 text-white transition-colors duration-150",
!@checkout_disabled && "bg-indigo-600 hover:bg-indigo-500", !@checkout_disabled && "bg-indigo-600 hover:bg-indigo-500",
@checkout_disabled && "pointer-events-none bg-gray-400 dark:bg-gray-600" @checkout_disabled && "pointer-events-none bg-gray-400 dark:bg-gray-600"
]} ]}

View File

@ -26,7 +26,7 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
<div <div
id={"#{@kind}-plan-box"} id={"#{@kind}-plan-box"}
class={[ class={[
"shadow-lg border border-gray-200 dark:border-none bg-white rounded-xl px-6 sm:px-4 py-4 sm:py-3 dark:bg-gray-800", "shadow-lg border border-gray-200 dark:border-none bg-white rounded-xl px-6 sm:px-4 py-4 sm:py-3 dark:bg-gray-850",
!@highlight && "dark:ring-gray-600", !@highlight && "dark:ring-gray-600",
@highlight && "ring-2 ring-indigo-600 dark:ring-indigo-300" @highlight && "ring-2 ring-indigo-600 dark:ring-indigo-300"
]} ]}
@ -63,7 +63,7 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
<div <div
id="enterprise-plan-box" id="enterprise-plan-box"
class={[ class={[
"rounded-xl px-6 sm:px-4 py-4 sm:py-3 bg-gray-900 shadow-xl dark:bg-gray-800", "rounded-xl px-6 sm:px-4 py-4 sm:py-3 bg-gray-900 shadow-xl dark:bg-gray-850",
!@recommended && "dark:ring-gray-600", !@recommended && "dark:ring-gray-600",
@recommended && "ring-4 ring-indigo-500 dark:ring-2 dark:ring-indigo-300" @recommended && "ring-4 ring-indigo-500 dark:ring-2 dark:ring-indigo-300"
]} ]}
@ -197,7 +197,7 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
<span <span
id={"#{@kind}-price-tag-interval"} id={"#{@kind}-price-tag-interval"}
class="text-sm font-semibold leading-6 text-gray-600 pl-1 self-end" class="text-sm font-semibold leading-6 text-gray-600 dark:text-gray-500 pl-1 self-end"
> >
/year /year
</span> </span>
@ -205,7 +205,7 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
<div class="font-bold tracking-tight text-sm self-center"> <div class="font-bold tracking-tight text-sm self-center">
<span <span
id={"#{@kind}-discount-price-tag-strikethrough-amount"} id={"#{@kind}-discount-price-tag-strikethrough-amount"}
class="line-through tracking-tight text-gray-500 dark:text-gray-600" class="line-through tracking-tight text-gray-500"
> >
{@monthly_cost} {@monthly_cost}
</span> </span>
@ -214,7 +214,7 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
</span> </span>
</div> </div>
<span class="text-sm font-semibold text-gray-600 pl-1 self-center"> <span class="text-sm font-semibold text-gray-600 dark:text-gray-500 pl-1 self-center">
/month /month
</span> </span>
</div> </div>

View File

@ -26,19 +26,19 @@ defmodule PlausibleWeb.Components.FlowProgress do
<div class="flex items-center text-base"> <div class="flex items-center text-base">
<div <div
:if={idx < @current_step_idx} :if={idx < @current_step_idx}
class="w-5 h-5 bg-green-500 dark:bg-green-600 text-white rounded-full flex items-center justify-center" class="size-6 bg-green-500 dark:bg-green-600 text-white rounded-full flex items-center justify-center"
> >
<Heroicons.check class="w-4 h-4" /> <Heroicons.check class="size-4" />
</div> </div>
<div <div
:if={idx == @current_step_idx} :if={idx == @current_step_idx}
class="w-5 h-5 bg-indigo-600 text-white rounded-full flex items-center justify-center font-semibold" class="size-6 bg-indigo-600 text-xs text-white font-bold rounded-full flex items-center justify-center"
> >
{idx + 1} {idx + 1}
</div> </div>
<div <div
:if={idx > @current_step_idx} :if={idx > @current_step_idx}
class="w-5 h-5 bg-gray-300 text-white dark:bg-gray-800 rounded-full flex items-center justify-center" class="size-6 bg-gray-300 text-xs text-white font-bold dark:bg-gray-800 rounded-full flex items-center justify-center"
> >
{idx + 1} {idx + 1}
</div> </div>

View File

@ -6,7 +6,7 @@ defmodule PlausibleWeb.Components.Generic do
@notice_themes %{ @notice_themes %{
gray: %{ gray: %{
bg: "bg-gray-150 dark:bg-gray-700/50", bg: "bg-gray-100 dark:bg-gray-800",
icon: "text-gray-600 dark:text-gray-300", icon: "text-gray-600 dark:text-gray-300",
title_text: "text-sm text-gray-900 dark:text-gray-100", title_text: "text-sm text-gray-900 dark:text-gray-100",
body_text: "text-sm text-gray-600 dark:text-gray-300 leading-5" body_text: "text-sm text-gray-600 dark:text-gray-300 leading-5"
@ -18,22 +18,23 @@ defmodule PlausibleWeb.Components.Generic do
body_text: "text-sm text-gray-600 dark:text-gray-100/60 leading-5" body_text: "text-sm text-gray-600 dark:text-gray-100/60 leading-5"
}, },
red: %{ red: %{
bg: "bg-red-100 dark:bg-red-900/50", bg: "bg-red-100 dark:bg-red-900/30",
icon: "text-red-600", icon: "text-red-600 dark:text-red-500",
title_text: "text-sm text-gray-900 dark:text-gray-100", title_text: "text-sm text-gray-900 dark:text-gray-100",
body_text: "text-sm text-gray-600 dark:text-gray-100/60 leading-5" body_text: "text-sm text-gray-600 dark:text-gray-100/60 leading-5"
} }
} }
@button_themes %{ @button_themes %{
"primary" => "bg-indigo-600 text-white hover:bg-indigo-700 focus-visible:outline-indigo-600", "primary" =>
"bright" => "bg-indigo-600 text-white hover:bg-indigo-700 focus-visible:outline-indigo-600 disabled:bg-indigo-400/60 disabled:dark:bg-indigo-600/30 disabled:dark:text-white/35",
"border border-gray-200 bg-gray-100 dark:bg-gray-300 text-gray-800 hover:bg-gray-200 focus-visible:outline-gray-100", "secondary" =>
"border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-700 text-gray-800 dark:text-gray-100 hover:text-gray-900 hover:shadow-sm dark:hover:bg-gray-600 dark:hover:text-white disabled:text-gray-700/40 disabled:hover:shadow-none dark:disabled:text-gray-500 dark:disabled:bg-gray-800 dark:disabled:border-gray-800",
"danger" => "danger" =>
"border border-gray-300 dark:border-gray-500 text-red-700 bg-white dark:bg-gray-900 hover:text-red-500 dark:hover:text-red-400 focus:border-blue-300 dark:text-red-500 active:text-red-800" "border border-gray-300 dark:border-gray-800 text-red-600 bg-white dark:bg-gray-800 hover:text-red-700 hover:shadow-sm dark:hover:text-red-400 dark:text-red-500 active:text-red-800 disabled:text-red-700/40 disabled:hover:shadow-none dark:disabled:text-red-500/35 dark:disabled:bg-gray-800"
} }
@button_base_class "whitespace-nowrap truncate inline-flex items-center justify-center gap-x-2 font-medium rounded-md px-3.5 py-2.5 text-sm shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:bg-gray-400 dark:disabled:text-white dark:disabled:text-gray-400 dark:disabled:bg-gray-700" @button_base_class "whitespace-nowrap truncate inline-flex items-center justify-center gap-x-2 font-medium rounded-md px-3.5 py-2.5 text-sm transition-all duration-150 cursor-pointer disabled:cursor-not-allowed"
attr(:type, :string, default: "button") attr(:type, :string, default: "button")
attr(:theme, :string, default: "primary") attr(:theme, :string, default: "primary")
@ -94,7 +95,7 @@ defmodule PlausibleWeb.Components.Generic do
theme_class = theme_class =
if assigns.disabled do if assigns.disabled do
"bg-gray-400 text-white dark:text-white dark:text-gray-400 dark:bg-gray-700 cursor-not-allowed" "bg-gray-400 text-white transition-colors duration-150 dark:text-white dark:text-gray-400 dark:bg-gray-700 cursor-not-allowed"
else else
@button_themes[assigns.theme] @button_themes[assigns.theme]
end end
@ -141,7 +142,12 @@ defmodule PlausibleWeb.Components.Generic do
<:tooltip_content> <:tooltip_content>
<span>Learn more</span> <span>Learn more</span>
</:tooltip_content> </:tooltip_content>
<a href={"https://plausible.io/docs/#{@slug}"} rel="noopener noreferrer" target="_blank"> <a
href={"https://plausible.io/docs/#{@slug}"}
rel="noopener noreferrer"
target="_blank"
class="inline-block"
>
<Heroicons.information_circle class="text-gray-400 dark:text-indigo-500 size-5 hover:text-indigo-500 dark:hover:text-indigo-400 transition-colors duration-150" /> <Heroicons.information_circle class="text-gray-400 dark:text-indigo-500 size-5 hover:text-indigo-500 dark:hover:text-indigo-400 transition-colors duration-150" />
</a> </a>
</.tooltip> </.tooltip>
@ -157,9 +163,9 @@ defmodule PlausibleWeb.Components.Generic do
def upgrade(assigns) do def upgrade(assigns) do
~H""" ~H"""
<div class={["rounded-md p-5 bg-gray-100 dark:bg-gray-700/50", @class]} {@rest}> <div class={["rounded-md p-5 bg-gray-100 dark:bg-gray-800", @class]} {@rest}>
<div class="flex flex-col gap-y-4"> <div class="flex flex-col gap-y-4">
<div class="flex-shrink-0 bg-white dark:bg-gray-700 max-w-max rounded-md p-2 border border-gray-200 dark:border-gray-600 text-indigo-500 dark:text-indigo-400"> <div class="flex-shrink-0 bg-white dark:bg-gray-700 max-w-max rounded-md p-2 border border-gray-200 dark:border-gray-600 text-indigo-500">
{render_slot(@icon)} {render_slot(@icon)}
</div> </div>
<div class="flex flex-col gap-y-2"> <div class="flex flex-col gap-y-2">
@ -315,7 +321,7 @@ defmodule PlausibleWeb.Components.Generic do
slot(:inner_block, required: true) slot(:inner_block, required: true)
@base_class "block rounded-md text-sm/6 text-gray-900 ui-disabled:text-gray-500 dark:text-gray-100 dark:ui-disabled:text-gray-400 px-3 py-1.5" @base_class "block rounded-md text-sm/6 text-gray-900 ui-disabled:text-gray-500 dark:text-gray-100 dark:ui-disabled:text-gray-400 px-3 py-1.5"
@clickable_class "hover:bg-gray-100 dark:hover:bg-gray-700" @clickable_class "hover:bg-gray-100 dark:hover:bg-gray-700/80"
def dropdown_item(assigns) do def dropdown_item(assigns) do
assigns = assigns =
if assigns[:disabled] do if assigns[:disabled] do
@ -451,12 +457,12 @@ defmodule PlausibleWeb.Components.Generic do
> >
<span <span
class="relative inline-flex h-6 w-11 shrink-0 rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-hidden focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" class="relative inline-flex h-6 w-11 shrink-0 rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-hidden focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2"
x-bind:class={"#{@js_active_var} ? 'bg-indigo-600' : 'dark:bg-gray-700 bg-gray-200'"} x-bind:class={"#{@js_active_var} ? 'bg-indigo-600' : 'dark:bg-gray-600 bg-gray-200'"}
> >
<span <span
aria-hidden="true" aria-hidden="true"
class="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow-sm ring-0 transition duration-200 ease-in-out" class="pointer-events-none inline-block size-5 transform rounded-full bg-white shadow-sm ring-0 transition duration-200 ease-in-out"
x-bind:class={"#{@js_active_var} ? 'dark:bg-gray-800 translate-x-5' : 'dark:bg-gray-800 translate-x-0'"} x-bind:class={"#{@js_active_var} ? 'dark:bg-white translate-x-5' : 'dark:bg-white translate-x-0'"}
/> />
</span> </span>
</button> </button>
@ -484,7 +490,7 @@ defmodule PlausibleWeb.Components.Generic do
def tile(assigns) do def tile(assigns) do
~H""" ~H"""
<div class="shadow-sm bg-white dark:bg-gray-800 rounded-md mb-6"> <div class="shadow-sm bg-white dark:bg-gray-900 rounded-md mb-6">
<header class="relative py-4 px-6"> <header class="relative py-4 px-6">
<.title> <.title>
{render_slot(@title)} {render_slot(@title)}
@ -542,10 +548,8 @@ defmodule PlausibleWeb.Components.Generic do
"top-0", "top-0",
"-translate-y-full", "-translate-y-full",
"z-[1000]", "z-[1000]",
"sm:min-w-48",
"sm:max-w-72", "sm:max-w-72",
"whitespace-normal", "w-max"
"break-words"
] ]
tooltip_position_classes = tooltip_position_classes =
@ -582,7 +586,7 @@ defmodule PlausibleWeb.Components.Generic do
x-transition:leave-start="opacity-100" x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0" x-transition:leave-end="opacity-0"
> >
<div class="bg-gray-900 text-white rounded-sm px-2.5 py-1.5 text-xs font-medium"> <div class="bg-gray-800 text-white rounded-sm px-2.5 py-1.5 text-xs font-medium">
{render_slot(@tooltip_content)} {render_slot(@tooltip_content)}
</div> </div>
</div> </div>
@ -700,7 +704,7 @@ defmodule PlausibleWeb.Components.Generic do
def focus_box(assigns) do def focus_box(assigns) do
~H""" ~H"""
<div <div
class="bg-white w-full max-w-lg mx-auto dark:bg-gray-800 text-gray-900 dark:text-gray-100 shadow-md rounded-md mt-12" class="bg-white w-full max-w-lg mx-auto dark:bg-gray-900 text-gray-900 dark:text-gray-100 shadow-md rounded-md mt-12"
{@rest} {@rest}
> >
<div class={if(@padding?, do: "p-8")}> <div class={if(@padding?, do: "p-8")}>
@ -725,7 +729,7 @@ defmodule PlausibleWeb.Components.Generic do
:if={@footer != []} :if={@footer != []}
class="flex flex-col dark:text-gray-200 border-t border-gray-300 dark:border-gray-700" class="flex flex-col dark:text-gray-200 border-t border-gray-300 dark:border-gray-700"
> >
<div class="p-8"> <div class="p-8 text-sm">
{render_slot(@footer)} {render_slot(@footer)}
</div> </div>
</div> </div>
@ -832,8 +836,8 @@ defmodule PlausibleWeb.Components.Generic do
<button <button
type="submit" type="submit"
class={[ class={[
"relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full transition-colors ease-in-out duration-200 focus:outline-none focus:ring", "relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full transition-colors ease-in-out duration-200",
if(@set_to, do: "bg-indigo-600", else: "bg-gray-200 dark:bg-gray-700"), if(@set_to, do: "bg-indigo-600", else: "bg-gray-200 dark:bg-gray-600"),
if(@disabled?, do: "cursor-not-allowed") if(@disabled?, do: "cursor-not-allowed")
]} ]}
disabled={@disabled?} disabled={@disabled?}
@ -841,7 +845,7 @@ defmodule PlausibleWeb.Components.Generic do
<span <span
aria-hidden="true" aria-hidden="true"
class={[ class={[
"inline-block h-5 w-5 rounded-full bg-white dark:bg-gray-800 shadow transform transition ease-in-out duration-200", "inline-block size-5 rounded-full bg-white shadow transform transition ease-in-out duration-200",
if(@set_to, do: "translate-x-5", else: "translate-x-0") if(@set_to, do: "translate-x-5", else: "translate-x-0")
]} ]}
/> />
@ -920,7 +924,7 @@ defmodule PlausibleWeb.Components.Generic do
def filter_bar(assigns) do def filter_bar(assigns) do
~H""" ~H"""
<div class="mb-6 flex items-center justify-between" x-data> <div class="mb-6 flex items-center justify-between" x-data>
<div :if={@filtering_enabled?} class="relative rounded-md shadow-xs flex"> <div :if={@filtering_enabled?} class="relative rounded-md flex">
<form id="filter-form" phx-change="filter" phx-submit="filter" class="flex items-center"> <form id="filter-form" phx-change="filter" phx-submit="filter" class="flex items-center">
<div class="text-gray-800 inline-flex items-center"> <div class="text-gray-800 inline-flex items-center">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
@ -930,7 +934,7 @@ defmodule PlausibleWeb.Components.Generic do
type="text" type="text"
name="filter-text" name="filter-text"
id="filter-text" id="filter-text"
class="w-36 sm:w-full pl-8 text-sm shadow-xs dark:bg-gray-900 dark:text-gray-300 focus:ring-indigo-500 focus:border-indigo-500 block border-gray-300 dark:border-gray-500 rounded-md dark:bg-gray-800" class="w-36 sm:w-full pl-8 text-sm dark:bg-gray-750 dark:text-gray-300 focus:ring-indigo-500 focus:border-indigo-500 block border-gray-300 dark:border-gray-750 rounded-md dark:placeholder:text-gray-400 focus:outline-none focus:ring-3 focus:ring-indigo-500/20 dark:focus:ring-indigo-500/25 focus:border-indigo-500"
placeholder="Press / to search" placeholder="Press / to search"
x-ref="filter_text" x-ref="filter_text"
phx-debounce={200} phx-debounce={200}

View File

@ -94,7 +94,7 @@ defmodule PlausibleWeb.Components.Layout do
<% else %> <% else %>
<.settings_tab icon={@icon} text={@text} /> <.settings_tab icon={@icon} text={@text} />
<div class="ml-6"> <div class="flex flex-col gap-0.5 ml-7">
<.settings_tab <.settings_tab
:for={%{key: key, value: value} = opts <- @value} :for={%{key: key, value: value} = opts <- @value}
selected_fn={@selected_fn} selected_fn={@selected_fn}
@ -128,9 +128,9 @@ defmodule PlausibleWeb.Components.Layout do
class={[ class={[
"text-sm flex items-center px-2 py-2 leading-5 font-medium rounded-md outline-none focus:outline-none transition ease-in-out duration-150", "text-sm flex items-center px-2 py-2 leading-5 font-medium rounded-md outline-none focus:outline-none transition ease-in-out duration-150",
@current_tab? && @current_tab? &&
"text-gray-900 dark:text-gray-100 bg-gray-150 font-semibold dark:bg-gray-700/50 hover:text-gray-900 dark:hover:text-gray-100 focus:bg-gray-200 dark:focus:bg-gray-800", "text-gray-900 dark:text-gray-100 bg-gray-150 font-semibold dark:bg-gray-850 hover:text-gray-900 dark:hover:text-gray-100 focus:bg-gray-200 dark:focus:bg-gray-800",
@value && not @current_tab? && @value && not @current_tab? &&
"text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-800 focus:text-gray-900 focus:bg-gray-50 dark:focus:text-gray-100 dark:focus:bg-gray-800", "text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-850 focus:text-gray-900 focus:bg-gray-50 dark:focus:text-gray-100 dark:focus:bg-gray-800",
!@value && "text-gray-600 dark:text-gray-300" !@value && "text-gray-600 dark:text-gray-300"
]} ]}
> >

View File

@ -163,7 +163,7 @@ defmodule PlausibleWeb.Components.TwoFactor do
type="button" type="button"
x-on:click={"#{@state_param} = false"} x-on:click={"#{@state_param} = false"}
class="w-full sm:w-auto mr-2" class="w-full sm:w-auto mr-2"
theme="bright" theme="secondary"
> >
Cancel Cancel
</.button> </.button>

View File

@ -146,7 +146,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
<div class="mt-6 w-full md:flex"> <div class="mt-6 w-full md:flex">
<a <a
href={Routes.settings_path(PlausibleWeb.Endpoint, :subscription)} href={Routes.settings_path(PlausibleWeb.Endpoint, :subscription)}
class="hidden md:flex md:w-1/6 h-max md:mt-2 text-indigo-600 hover:text-indigo-700 dark:text-indigo-500 dark:hover:text-indigo-600 text-sm font-semibold gap-1 items-center" class="hidden md:flex md:w-1/6 h-max md:mt-2 text-indigo-600 hover:text-indigo-700 dark:text-indigo-500 dark:hover:text-indigo-400 text-sm font-semibold gap-1 items-center transition-colors duration-150"
> >
<span></span> <span></span>
<p>Back to settings</p> <p>Back to settings</p>
@ -222,7 +222,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
title="What's my current usage?" title="What's my current usage?"
title_class="text-gray-900 dark:text-gray-200" title_class="text-gray-900 dark:text-gray-200"
> >
<p class="text-gray-600 dark:text-gray-300"> <p class="text-gray-600 dark:text-gray-400">
<.render_usage pageview_usage={@usage.monthly_pageviews} /> <.render_usage pageview_usage={@usage.monthly_pageviews} />
</p> </p>
</.accordion_item> </.accordion_item>
@ -232,7 +232,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
title="What happens if I go over my monthly pageview limit?" title="What happens if I go over my monthly pageview limit?"
title_class="text-gray-900 dark:text-gray-200" title_class="text-gray-900 dark:text-gray-200"
> >
<p class="text-gray-600 dark:text-gray-300"> <p class="text-gray-600 dark:text-gray-400">
You will never be charged extra for an occasional traffic spike. There are no surprise fees and your card will never be charged unexpectedly. If your pageviews exceed your plan for two consecutive months, we will contact you to upgrade to a higher plan for the following month. You will have two weeks to make a decision. You can decide to continue with a higher plan or to cancel your account at that point. You will never be charged extra for an occasional traffic spike. There are no surprise fees and your card will never be charged unexpectedly. If your pageviews exceed your plan for two consecutive months, we will contact you to upgrade to a higher plan for the following month. You will have two weeks to make a decision. You can decide to continue with a higher plan or to cancel your account at that point.
</p> </p>
</.accordion_item> </.accordion_item>
@ -258,7 +258,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
</span> </span>
Please see your full usage report (including sites and team members) under the Please see your full usage report (including sites and team members) under the
<a <a
class="text-indigo-600 inline hover:underline" class="inline font-medium text-indigo-600 dark:text-indigo-500 hover:text-indigo-700 dark:hover:text-indigo-400 transition-colors duration-150"
href={Routes.settings_path(PlausibleWeb.Endpoint, :subscription)} href={Routes.settings_path(PlausibleWeb.Endpoint, :subscription)}
> >
"Subscription" section "Subscription" section
@ -361,8 +361,19 @@ defmodule PlausibleWeb.Live.ChoosePlan do
~H""" ~H"""
<div class="mt-16 -mb-16 text-center"> <div class="mt-16 -mb-16 text-center">
Any other questions? Any other questions?
<a class="text-indigo-600 hover:underline" href={contact_link()}>Contact us</a> <a
or see <a class="text-indigo-600 hover:underline" href={billing_faq_link()}>billing FAQ</a> class="font-medium text-indigo-600 hover:text-indigo-700 dark:text-indigo-500 dark:hover:text-indigo-400 transition-colors duration-150"
href={contact_link()}
>
Contact us
</a>
or see
<a
class="font-medium text-indigo-600 hover:text-indigo-700 dark:text-indigo-500 dark:hover:text-indigo-400 transition-colors duration-150"
href={billing_faq_link()}
>
billing FAQ
</a>
</div> </div>
""" """
end end

View File

@ -90,7 +90,7 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
<div class="relative w-full"> <div class="relative w-full">
<div <div
@click.away="close" @click.away="close"
class="pl-2 pr-8 py-1 w-full dark:bg-gray-900 dark:text-gray-300 rounded-md shadow-xs border border-gray-300 dark:border-gray-700 focus-within:border-indigo-500 focus-within:ring-1 focus-within:ring-indigo-500" class="pl-2 pr-8 py-1 w-full dark:bg-gray-750 dark:text-gray-300 rounded-md shadow-xs border border-gray-300 dark:border-gray-750 focus-within:outline-none focus-within:ring-3 focus-within:ring-indigo-500/20 dark:focus-within:ring-indigo-500/25 focus-within:border-indigo-500"
> >
<input <input
type="text" type="text"
@ -180,7 +180,7 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
id={"dropdown-#{@ref}"} id={"dropdown-#{@ref}"}
x-show="isOpen" x-show="isOpen"
x-ref="suggestions" x-ref="suggestions"
class="text-sm w-full dropdown z-50 absolute mt-1 max-h-60 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1/5 ring-black focus:outline-hidden dark:bg-gray-900" class="text-sm w-full dropdown z-50 absolute mt-1 max-h-60 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1/5 ring-black focus:outline-hidden dark:bg-gray-800"
style="display: none;" style="display: none;"
> >
<.option <.option
@ -242,7 +242,7 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
@creatable && "creatable" @creatable && "creatable"
]} ]}
@mouseenter={"setFocus(#{@idx})"} @mouseenter={"setFocus(#{@idx})"}
x-bind:class={ "{'text-white bg-indigo-500': focus === #{@idx}}" } x-bind:class={ "{'bg-gray-100 dark:bg-gray-700': focus === #{@idx}}" }
id={"dropdown-#{@ref}-option-#{@idx}"} id={"dropdown-#{@ref}-option-#{@idx}"}
> >
<a <a

View File

@ -18,7 +18,7 @@ defmodule PlausibleWeb.Live.Components.Form do
<.input name="my-input" errors={["oh no!"]} /> <.input name="my-input" errors={["oh no!"]} />
""" """
@default_input_class "text-sm text-gray-900 dark:text-white dark:bg-gray-900 block pl-3.5 py-2.5 border-gray-300 dark:border-gray-700 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 rounded-md" @default_input_class "text-sm text-gray-900 dark:text-white dark:bg-gray-750 block pl-3.5 py-2.5 border-gray-300 dark:border-gray-800 transition-all duration-150 focus:outline-none focus:ring-3 focus:ring-indigo-500/20 dark:focus:ring-indigo-500/25 focus:border-indigo-500 rounded-md disabled:bg-gray-100 disabled:dark:bg-gray-800 disabled:border-gray-200 disabled:dark:border-gray-800 disabled:text-gray-900/40 disabled:dark:text-white/30 disabled:cursor-not-allowed"
attr(:id, :any, default: nil) attr(:id, :any, default: nil)
attr(:name, :any) attr(:name, :any)
@ -110,7 +110,7 @@ defmodule PlausibleWeb.Live.Components.Form do
checked={@checked} checked={@checked}
id={@id} id={@id}
name={@name} name={@name}
class="block h-5 w-5 rounded-sm dark:bg-gray-700 border-gray-300 text-indigo-600 focus:ring-indigo-600" class="block size-5 rounded-sm dark:bg-gray-600 border-gray-300 dark:border-gray-600 text-indigo-600"
{@rest} {@rest}
/> />
{@label} {@label}
@ -130,7 +130,7 @@ defmodule PlausibleWeb.Live.Components.Form do
id={@id} id={@id}
name={@name} name={@name}
checked={assigns[:checked]} checked={assigns[:checked]}
class="block dark:bg-gray-900 h-4 w-4 mt-[0.15rem] cursor-pointer text-indigo-600 border-gray-300 dark:border-gray-500 focus:ring-indigo-500" class="block dark:bg-gray-900 size-4.5 mt-px cursor-pointer text-indigo-600 border-gray-400 dark:border-gray-600 checked:border-indigo-600 dark:checked:border-white"
{@rest} {@rest}
/> />
<.label :if={@label} class="flex flex-col flex-inline" for={@id}> <.label :if={@label} class="flex flex-col flex-inline" for={@id}>
@ -230,9 +230,9 @@ defmodule PlausibleWeb.Live.Components.Form do
<a <a
onclick={"var input = document.getElementById('#{@id}'); input.focus(); input.select(); document.execCommand('copy'); event.stopPropagation();"} onclick={"var input = document.getElementById('#{@id}'); input.focus(); input.select(); document.execCommand('copy'); event.stopPropagation();"}
href="javascript:void(0)" href="javascript:void(0)"
class="absolute flex items-center text-xs font-medium text-indigo-600 no-underline hover:underline top-3 right-4" class="absolute flex items-center text-xs font-medium text-indigo-600 dark:text-indigo-500 no-underline hover:text-indigo-700 dark:hover:text-indigo-400 top-3 right-4 transition-colors duration-150"
> >
<Heroicons.document_duplicate class="pr-1 text-indigo-600 dark:text-indigo-500 w-5 h-5" /> <Heroicons.document_duplicate class="pr-1 size-5" />
<span> <span>
COPY COPY
</span> </span>

View File

@ -163,7 +163,7 @@ defmodule PlausibleWeb.Live.Components.Modal do
def render(assigns) do def render(assigns) do
class = [ class = [
"md:w-1/2 w-full max-w-md mx-auto bg-white dark:bg-gray-800 shadow-xl rounded-lg px-8 pt-6 pb-8 top-24", "md:w-1/2 w-full max-w-md mx-auto bg-white dark:bg-gray-900 shadow-xl rounded-lg px-8 pt-6 pb-8 top-24",
assigns.class assigns.class
] ]

View File

@ -180,7 +180,7 @@ defmodule PlausibleWeb.Live.Components.Verification do
<a <a
href="#" href="#"
@click.prevent="showDiagnostics = !showDiagnostics" @click.prevent="showDiagnostics = !showDiagnostics"
class="bg-yellow-100 dark:text-gray-800" class="bg-yellow-100 dark:bg-yellow-800/40"
> >
As a super-admin, you're eligible to see diagnostics details. Click to expand. As a super-admin, you're eligible to see diagnostics details. Click to expand.
</a> </a>

View File

@ -98,10 +98,10 @@ defmodule PlausibleWeb.Live.CSVImport do
~H""" ~H"""
<label <label
phx-drop-target={@upload.ref} phx-drop-target={@upload.ref}
class="block border-2 dark:border-gray-600 rounded-md p-4 hover:bg-gray-50 dark:hover:bg-gray-900 hover:border-indigo-500 dark:hover:border-indigo-600 transition cursor-pointer" class="block border border-gray-300 dark:border-gray-750 dark:bg-gray-750 rounded-md p-4 transition cursor-pointer"
> >
<div class="hidden md:flex items-center text-gray-500 dark:text-gray-500"> <div class="hidden md:flex items-center text-gray-500 dark:text-gray-400">
<Heroicons.document_plus class="w-5 h-5 transition" /> <Heroicons.document_plus class="size-5 transition" />
<span class="ml-1.5 text-sm"> <span class="ml-1.5 text-sm">
(or drag-and-drop your unzipped CSVs here) (or drag-and-drop your unzipped CSVs here)
</span> </span>

View File

@ -66,7 +66,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
def edit_form(assigns) do def edit_form(assigns) do
~H""" ~H"""
<.form :let={f} for={@form} phx-submit="save-goal" phx-target={@myself}> <.form :let={f} for={@form} phx-submit="save-goal" phx-target={@myself}>
<.title>Edit Goal for {@domain}</.title> <.title>Edit goal for {@domain}</.title>
<.custom_event_fields <.custom_event_fields
:if={@selected_tab == "custom_events"} :if={@selected_tab == "custom_events"}
@ -96,7 +96,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
/> />
<.button type="submit" class="w-full"> <.button type="submit" class="w-full">
Update Goal Update goal
</.button> </.button>
</.form> </.form>
""" """
@ -111,9 +111,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
phx-submit="save-goal" phx-submit="save-goal"
phx-target={@myself} phx-target={@myself}
> >
<.spinner class="spinner block absolute right-9 top-8" x-show="tabSelectionInProgress" /> <.title>Add goal for {@domain}</.title>
<.title>Add Goal for {@domain}</.title>
<.tabs current_user={@current_user} site={@site} selected_tab={@selected_tab} myself={@myself} /> <.tabs current_user={@current_user} site={@site} selected_tab={@selected_tab} myself={@myself} />
@ -149,7 +147,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
<div x-show="!tabSelectionInProgress"> <div x-show="!tabSelectionInProgress">
<.button type="submit" class="w-full"> <.button type="submit" class="w-full">
Add Goal Add goal
</.button> </.button>
</div> </div>
@ -190,7 +188,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
</div> </div>
<.label for={"page_path_input_#{@suffix}"}> <.label for={"page_path_input_#{@suffix}"}>
Page Path Page path
</.label> </.label>
<.live_component <.live_component
@ -211,7 +209,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
</.error> </.error>
<.input <.input
label="Display Name" label="Display name"
id="pageview_display_name_input" id="pageview_display_name_input"
field={@f[:display_name]} field={@f[:display_name]}
type="text" type="text"
@ -268,7 +266,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
</div> </div>
<.label for={"scroll_threshold_input_#{@suffix}"}> <.label for={"scroll_threshold_input_#{@suffix}"}>
Scroll Percentage Threshold (1-100) Scroll percentage threshold (1-100)
</.label> </.label>
<.input <.input
@ -284,7 +282,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
/> />
<.label for={"scroll_page_path_input_#{@suffix}"} class="mt-3"> <.label for={"scroll_page_path_input_#{@suffix}"} class="mt-3">
Page Path Page path
</.label> </.label>
<.live_component <.live_component
@ -305,7 +303,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
</.error> </.error>
<.input <.input
label="Display Name" label="Display name"
id="scroll_display_name_input" id="scroll_display_name_input"
field={@f[:display_name]} field={@f[:display_name]}
type="text" type="text"
@ -344,7 +342,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
<div> <div>
<.label for={"event_name_input_#{@suffix}"}> <.label for={"event_name_input_#{@suffix}"}>
Event Name Event name
</.label> </.label>
<.live_component <.live_component
@ -369,7 +367,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
<div class="mt-2"> <div class="mt-2">
<.input <.input
label="Display Name" label="Display name"
id="custom_event_display_name_input" id="custom_event_display_name_input"
field={@f[:display_name]} field={@f[:display_name]}
type="text" type="text"
@ -447,69 +445,58 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
def tabs(assigns) do def tabs(assigns) do
~H""" ~H"""
<div class="text-sm mt-6 font-medium dark:text-gray-100">Goal Trigger</div> <div class="text-sm mt-6 font-medium dark:text-gray-100">Goal trigger</div>
<div class="my-2 text-sm w-full flex rounded-sm border border-gray-300 dark:border-gray-500 overflow-hidden"> <div class="my-2 text-sm w-full flex gap-1 overflow-hidden">
<.custom_events_tab selected?={@selected_tab == "custom_events"} myself={@myself} /> <.tab
<.pageviews_tab selected?={@selected_tab == "pageviews"} myself={@myself} /> id="event-tab"
<.scroll_tab selected?={@selected_tab == "scroll"} myself={@myself} /> tab_value="custom_events"
selected?={@selected_tab == "custom_events"}
myself={@myself}
>
Custom event
</.tab>
<.tab
id="pageview-tab"
tab_value="pageviews"
selected?={@selected_tab == "pageviews"}
myself={@myself}
>
Pageview
</.tab>
<.tab
id="scroll-tab"
tab_value="scroll"
selected?={@selected_tab == "scroll"}
myself={@myself}
>
Scroll depth
</.tab>
</div> </div>
""" """
end end
defp custom_events_tab(assigns) do attr(:id, :string, required: true)
attr(:tab_value, :string, required: true)
attr(:selected?, :boolean, required: true)
attr(:myself, :any, required: true)
slot(:inner_block, required: true)
defp tab(assigns) do
~H""" ~H"""
<a <a
class={[ class={[
"flex-1 text-center py-2.5 border-r dark:border-gray-500", "flex-1 text-center py-2.5 rounded-md font-medium hover:bg-gray-100 dark:hover:bg-gray-750 transition-colors duration-150",
"cursor-pointer", "cursor-pointer",
@selected? && "shadow-inner font-medium bg-indigo-600 text-white", @selected? && "bg-gray-150 dark:bg-gray-700 text-gray-800 dark:text-white",
!@selected? && "dark:text-gray-100 text-gray-800" !@selected? && "dark:text-gray-200 text-gray-600 hover:text-gray-800 dark:hover:text-white"
]} ]}
id="event-tab" id={@id}
x-on:click={!@selected? && "tabSelectionInProgress = true"} x-on:click={!@selected? && "tabSelectionInProgress = true"}
phx-click="switch-tab" phx-click="switch-tab"
phx-value-tab="custom_events" phx-value-tab={@tab_value}
phx-target={@myself} phx-target={@myself}
> >
Custom Event {render_slot(@inner_block)}
</a>
"""
end
def pageviews_tab(assigns) do
~H"""
<a
class={[
"flex-1 text-center py-2.5 cursor-pointer",
@selected? && "shadow-inner font-medium bg-indigo-600 text-white",
!@selected? && "dark:text-gray-100 text-gray-800"
]}
id="pageview-tab"
x-on:click={!@selected? && "tabSelectionInProgress = true"}
phx-click="switch-tab"
phx-value-tab="pageviews"
phx-target={@myself}
>
Pageview
</a>
"""
end
def scroll_tab(assigns) do
~H"""
<a
class={[
"flex-1 text-center py-2.5 cursor-pointer border-l dark:border-gray-500",
@selected? && "shadow-inner font-medium bg-indigo-600 text-white",
!@selected? && "dark:text-gray-100 text-gray-800"
]}
id="scroll-tab"
x-on:click={!@selected? && "tabSelectionInProgress = true"}
phx-click="switch-tab"
phx-value-tab="scroll"
phx-target={@myself}
>
Scroll Depth
</a> </a>
""" """
end end
@ -628,7 +615,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
else: "text-gray-500 dark:text-gray-400" else: "text-gray-500 dark:text-gray-400"
) )
]}> ]}>
Enable Revenue Tracking Enable revenue tracking
</span> </span>
</div> </div>
</.tooltip> </.tooltip>

View File

@ -24,7 +24,7 @@ defmodule PlausibleWeb.Live.GoalSettings.List do
x-data x-data
x-on:click={Modal.JS.preopen("goals-form-modal")} x-on:click={Modal.JS.preopen("goals-form-modal")}
> >
Add Goal Add goal
</.button> </.button>
</.filter_bar> </.filter_bar>
@ -57,7 +57,7 @@ defmodule PlausibleWeb.Live.GoalSettings.List do
<span :if={goal.page_path && goal.scroll_threshold == -1}>Pageview</span> <span :if={goal.page_path && goal.scroll_threshold == -1}>Pageview</span>
<span :if={goal.event_name && !goal.currency}>Custom Event</span> <span :if={goal.event_name && !goal.currency}>Custom Event</span>
<span :if={goal.currency}>Revenue Goal ({goal.currency})</span> <span :if={goal.currency}>Revenue Goal ({goal.currency})</span>
<span :if={not Enum.empty?(goal.funnels)} class="text-gray-400 dark:text-gray-600"> <span :if={not Enum.empty?(goal.funnels)} class="text-gray-400 dark:text-gray-500">
<br />Belongs to funnel(s) <br />Belongs to funnel(s)
</span> </span>
</.td> </.td>
@ -127,19 +127,19 @@ defmodule PlausibleWeb.Live.GoalSettings.List do
~H""" ~H"""
<span <span
:if={@goal.page_path && @goal.scroll_threshold > -1} :if={@goal.page_path && @goal.scroll_threshold > -1}
class="block truncate text-gray-400 dark:text-gray-600" class="block truncate text-gray-400 dark:text-gray-500"
> >
{page_scroll_description(@goal)} {page_scroll_description(@goal)}
</span> </span>
<span <span
:if={@goal.page_path && @goal.scroll_threshold == -1} :if={@goal.page_path && @goal.scroll_threshold == -1}
class="block truncate text-gray-400 dark:text-gray-600" class="block truncate text-gray-400 dark:text-gray-500"
> >
{pageview_description(@goal)} {pageview_description(@goal)}
</span> </span>
<span :if={@goal.event_name} class="block truncate text-gray-400 dark:text-gray-600"> <span :if={@goal.event_name} class="block truncate text-gray-400 dark:text-gray-500">
{custom_event_description(@goal)} {custom_event_description(@goal)}
</span> </span>
""" """

View File

@ -76,7 +76,7 @@ defmodule PlausibleWeb.Live.ImportsExportsSettings do
<div class="flex gap-x-4"> <div class="flex gap-x-4">
<.button_link <.button_link
theme="bright" theme="secondary"
href={Plausible.Google.API.import_authorize_url(@site.id)} href={Plausible.Google.API.import_authorize_url(@site.id)}
disabled={@import_in_progress? or @at_maximum?} disabled={@import_in_progress? or @at_maximum?}
mt?={false} mt?={false}

View File

@ -121,7 +121,7 @@ defmodule PlausibleWeb.Live.Installation do
</div> </div>
</:loading> </:loading>
<div class="grid grid-cols-2 sm:flex sm:flex-row gap-2 bg-gray-100 dark:bg-gray-900 rounded-md p-1"> <div class="grid grid-cols-2 sm:flex sm:flex-row gap-1 rounded-md">
<.tab <.tab
patch={"?type=manual&flow=#{@flow}"} patch={"?type=manual&flow=#{@flow}"}
selected={@installation_type.result == "manual"} selected={@installation_type.result == "manual"}
@ -271,19 +271,18 @@ defmodule PlausibleWeb.Live.Installation do
slot :inner_block, required: true slot :inner_block, required: true
defp tab(assigns) do defp tab(assigns) do
assigns = base_classes =
"rounded-md px-3.5 py-2.5 text-sm font-medium flex items-center flex-1 justify-center whitespace-nowrap hover:bg-gray-100 dark:hover:bg-gray-750 transition-colors duration-150"
selected_class =
if assigns[:selected] do if assigns[:selected] do
assign(assigns, "bg-gray-150 dark:bg-gray-700"
class:
"bg-white dark:bg-gray-800 rounded-md px-3.5 py-2.5 text-sm font-medium flex items-center flex-1 justify-center whitespace-nowrap"
)
else else
assign(assigns, "bg-transparent cursor-pointer"
class:
"bg-gray-100 dark:bg-gray-700 rounded-md px-3.5 py-2.5 text-sm font-medium flex items-center cursor-pointer flex-1 justify-center whitespace-nowrap"
)
end end
assigns = assign(assigns, class: "#{selected_class} #{base_classes}")
~H""" ~H"""
<.link patch={@patch} class={@class}> <.link patch={@patch} class={@class}>
{render_slot(@inner_block)} {render_slot(@inner_block)}

View File

@ -193,16 +193,16 @@ defmodule PlausibleWeb.Live.Installation.Instructions do
<.tooltip sticky?={false}> <.tooltip sticky?={false}>
<:tooltip_content> <:tooltip_content>
{@tooltip} {@tooltip}
<br /><br />Click to learn more. <br />Click to learn more.
</:tooltip_content> </:tooltip_content>
<a href={@learn_more} target="_blank" rel="noopener noreferrer"> <a href={@learn_more} target="_blank" rel="noopener noreferrer">
<Heroicons.information_circle class="text-indigo-700 dark:text-gray-500 w-5 h-5 hover:stroke-2" /> <Heroicons.information_circle class="inline-block text-indigo-700 dark:text-gray-500 size-5 hover:stroke-2" />
</a> </a>
</.tooltip> </.tooltip>
</div> </div>
<div class="ml-2 visible md:invisible"> <div class="ml-2 visible md:invisible">
<a href={@learn_more} target="_blank" rel="noopener noreferrer"> <a href={@learn_more} target="_blank" rel="noopener noreferrer">
<Heroicons.information_circle class="text-indigo-700 dark:text-gray-500 w-5 h-5 hover:stroke-2" /> <Heroicons.information_circle class="inline-block text-indigo-700 dark:text-gray-500 size-5 hover:stroke-2" />
</a> </a>
</div> </div>
</div> </div>
@ -219,16 +219,16 @@ defmodule PlausibleWeb.Live.Installation.Instructions do
<.tooltip sticky?={false}> <.tooltip sticky?={false}>
<:tooltip_content> <:tooltip_content>
{@tooltip} {@tooltip}
<br /><br />Click to learn more. <br />Click to learn more.
</:tooltip_content> </:tooltip_content>
<a href={@learn_more} target="_blank" rel="noopener noreferrer"> <a href={@learn_more} target="_blank" rel="noopener noreferrer">
<Heroicons.information_circle class="text-indigo-700 dark:text-gray-500 w-5 h-5 hover:stroke-2" /> <Heroicons.information_circle class="inline-block text-indigo-700 dark:text-gray-500 size-5 hover:stroke-2" />
</a> </a>
</.tooltip> </.tooltip>
</div> </div>
<div class="ml-2 visible md:invisible"> <div class="ml-2 visible md:invisible">
<a href={@learn_more} target="_blank" rel="noopener noreferrer"> <a href={@learn_more} target="_blank" rel="noopener noreferrer">
<Heroicons.information_circle class="text-indigo-700 dark:text-gray-500 w-5 h-5 hover:stroke-2" /> <Heroicons.information_circle class="inline-block text-indigo-700 dark:text-gray-500 size-5 hover:stroke-2" />
</a> </a>
</div> </div>
</div> </div>
@ -241,7 +241,7 @@ defmodule PlausibleWeb.Live.Installation.Instructions do
<div class="relative"> <div class="relative">
<textarea <textarea
id="snippet" id="snippet"
class={"w-full border-1 border-gray-300 rounded-md p-4 text-sm text-gray-700 dark:border-gray-500 dark:bg-gray-900 dark:text-gray-300 #{if !@resizable, do: "resize-none"}"} class={"w-full border-1 border-gray-300 rounded-md p-4 text-sm text-gray-700 dark:border-gray-750 dark:bg-gray-750 dark:text-gray-300 #{if !@resizable, do: "resize-none"}"}
rows={@rows} rows={@rows}
readonly readonly
><%= @text %></textarea> ><%= @text %></textarea>
@ -249,9 +249,9 @@ defmodule PlausibleWeb.Live.Installation.Instructions do
<a <a
onclick="var input = document.getElementById('snippet'); input.focus(); input.select(); document.execCommand('copy'); event.stopPropagation();" onclick="var input = document.getElementById('snippet'); input.focus(); input.select(); document.execCommand('copy'); event.stopPropagation();"
href="javascript:void(0)" href="javascript:void(0)"
class="absolute flex items-center text-xs font-medium text-indigo-600 no-underline hover:underline bottom-2 right-4 p-2 bg-white dark:bg-gray-900" class="absolute flex items-center text-xs font-medium text-indigo-600 no-underline bottom-2 right-4 p-2 bg-white transition-colors duration-150 hover:text-indigo-700 dark:text-indigo-500 dark:hover:text-indigo-400 dark:bg-gray-750"
> >
<Heroicons.document_duplicate class="pr-1 text-indigo-600 dark:text-indigo-500 w-5 h-5" /> <Heroicons.document_duplicate class="pr-1 text-current size-5" />
<span> <span>
COPY COPY
</span> </span>

View File

@ -54,7 +54,7 @@ defmodule PlausibleWeb.Live.Plugins.API.TokenForm do
<div class="fixed inset-0 flex items-center justify-center mt-16 z-50 overflow-y-auto overflow-x-hidden"> <div class="fixed inset-0 flex items-center justify-center mt-16 z-50 overflow-y-auto overflow-x-hidden">
<div class="w-1/2 h-full"> <div class="w-1/2 h-full">
<div <div
class="max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded-sm px-8 pt-6 pb-8 mb-4 mt-8" class="max-w-md w-full mx-auto bg-white dark:bg-gray-900 shadow-md rounded-sm px-8 pt-6 pb-8 mb-4 mt-8"
phx-click-away="close-token-modal" phx-click-away="close-token-modal"
> >
<%= if @token_generated do %> <%= if @token_generated do %>
@ -67,21 +67,22 @@ defmodule PlausibleWeb.Live.Plugins.API.TokenForm do
/> />
</div> </div>
<p class="mt-4 text-sm text-gray-500 dark:text-gray-400"> <p class="mt-4 text-sm text-gray-500 dark:text-gray-400">
Please copy the token and store it in a secure place as it won't be shown again. Copy the token and store it in a secure place, as it won't be shown again.
<span :if={@token_description == "WordPress"}> <span :if={@token_description == "WordPress"}>
You'll need to paste the token in the settings area of the Plausible WordPress plugin. You'll need to paste the token in the settings area of the Plausible WordPress plugin.
</span> </span>
</p> </p>
<.button <.button
theme="secondary"
phx-click="close-token-modal" phx-click="close-token-modal"
class="w-full border !border-gray-300 dark:!border-gray-500 !text-gray-700 dark:!text-gray-300 !bg-transparent hover:!bg-gray-100 dark:hover:!bg-gray-850" class="w-full"
> >
Close modal Close modal
</.button> </.button>
<% else %> <% else %>
<.form :let={f} for={@form} phx-submit="generate-token"> <.form :let={f} for={@form} phx-submit="generate-token">
<.title> <.title>
Create Plugin Token for {@domain} Create plugin token for {@domain}
</.title> </.title>
<div class="mt-4"> <div class="mt-4">
@ -100,7 +101,7 @@ defmodule PlausibleWeb.Live.Plugins.API.TokenForm do
Once created, we will display the token so it can be copied. Once created, we will display the token so it can be copied.
</p> </p>
<.button type="submit" class="w-full"> <.button type="submit" class="w-full">
Create Plugin Token Create plugin token
</.button> </.button>
</.form> </.form>
<% end %> <% end %>

View File

@ -52,11 +52,11 @@ defmodule PlausibleWeb.Live.PropsSettings.Form do
<.form <.form
:let={f} :let={f}
for={@form} for={@form}
class="max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded-sm px-8 pt-6 pb-8 mb-4 mt-8" class="max-w-md w-full mx-auto bg-white dark:bg-gray-900 shadow-md rounded-sm px-8 pt-6 pb-8 mb-4 mt-8"
phx-submit="allow-prop" phx-submit="allow-prop"
phx-click-away="cancel-allow-prop" phx-click-away="cancel-allow-prop"
> >
<.title>Add Property for {@domain}</.title> <.title>Add property for {@domain}</.title>
<div class="mt-6"> <div class="mt-6">
<.label for="prop_input"> <.label for="prop_input">
@ -99,7 +99,7 @@ defmodule PlausibleWeb.Live.PropsSettings.Form do
</div> </div>
<.button type="submit" class="w-full"> <.button type="submit" class="w-full">
Add Property Add property
</.button> </.button>
<button <button

View File

@ -13,7 +13,7 @@ defmodule PlausibleWeb.Live.PropsSettings.List do
<div> <div>
<.filter_bar filter_text={@filter_text} placeholder="Search Properties"> <.filter_bar filter_text={@filter_text} placeholder="Search Properties">
<.button phx-click="add-prop" mt?={false}> <.button phx-click="add-prop" mt?={false}>
Add Property Add property
</.button> </.button>
</.filter_bar> </.filter_bar>
<%= if is_list(@props) && length(@props) > 0 do %> <%= if is_list(@props) && length(@props) > 0 do %>

View File

@ -106,9 +106,9 @@ defmodule PlausibleWeb.Live.Shields.CountryRules do
for={@form} for={@form}
phx-submit="save-country-rule" phx-submit="save-country-rule"
phx-target={@myself} phx-target={@myself}
class="max-w-md w-full mx-auto bg-white dark:bg-gray-800" class="max-w-md w-full mx-auto"
> >
<.title>Add Country to Block List</.title> <.title>Add country to block list</.title>
<.live_component <.live_component
class="mt-4" class="mt-4"
@ -126,7 +126,7 @@ defmodule PlausibleWeb.Live.Shields.CountryRules do
Once added, we will start rejecting traffic from this country within a few minutes. Once added, we will start rejecting traffic from this country within a few minutes.
</p> </p>
<.button type="submit" class="w-full"> <.button type="submit" class="w-full">
Add Country Add country
</.button> </.button>
</.form> </.form>
</.live_component> </.live_component>

View File

@ -120,9 +120,9 @@ defmodule PlausibleWeb.Live.Shields.HostnameRules do
for={@form} for={@form}
phx-submit="save-hostname-rule" phx-submit="save-hostname-rule"
phx-target={@myself} phx-target={@myself}
class="max-w-md w-full mx-auto bg-white dark:bg-gray-800" class="max-w-md w-full mx-auto"
> >
<.title>Add Hostname to Allow List</.title> <.title>Add hostname to allow list</.title>
<.live_component <.live_component
class="mt-8" class="mt-8"
@ -148,7 +148,7 @@ defmodule PlausibleWeb.Live.Shields.HostnameRules do
<% end %> <% end %>
</p> </p>
<.button type="submit" class="w-full"> <.button type="submit" class="w-full">
Add Hostname Add hostname
</.button> </.button>
</.form> </.form>
</.live_component> </.live_component>

View File

@ -121,9 +121,9 @@ defmodule PlausibleWeb.Live.Shields.IPRules do
for={@form} for={@form}
phx-submit="save-ip-rule" phx-submit="save-ip-rule"
phx-target={@myself} phx-target={@myself}
class="max-w-md w-full mx-auto bg-white dark:bg-gray-800" class="max-w-md w-full mx-auto"
> >
<.title>Add IP to Block List</.title> <.title>Add IP to block list</.title>
<div class="mt-4"> <div class="mt-4">
<p <p
@ -140,7 +140,7 @@ defmodule PlausibleWeb.Live.Shields.IPRules do
<.input <.input
autofocus autofocus
field={f[:inet]} field={f[:inet]}
label="IP Address" label="IP address"
placeholder="e.g. 192.168.127.12" placeholder="e.g. 192.168.127.12"
/> />
</div> </div>
@ -155,7 +155,7 @@ defmodule PlausibleWeb.Live.Shields.IPRules do
Once added, we will start rejecting traffic from this IP within a few minutes. Once added, we will start rejecting traffic from this IP within a few minutes.
</p> </p>
<.button type="submit" class="w-full"> <.button type="submit" class="w-full">
Add IP Address Add IP address
</.button> </.button>
</.form> </.form>
</.live_component> </.live_component>

View File

@ -115,9 +115,9 @@ defmodule PlausibleWeb.Live.Shields.PageRules do
for={@form} for={@form}
phx-submit="save-page-rule" phx-submit="save-page-rule"
phx-target={@myself} phx-target={@myself}
class="max-w-md w-full mx-auto bg-white dark:bg-gray-800" class="max-w-md w-full mx-auto"
> >
<.title>Add Page to Block List</.title> <.title>Add page to block list</.title>
<.live_component <.live_component
class="mt-4" class="mt-4"
@ -141,7 +141,7 @@ defmodule PlausibleWeb.Live.Shields.PageRules do
Once added, we will start rejecting traffic from this page within a few minutes. Once added, we will start rejecting traffic from this page within a few minutes.
</p> </p>
<.button type="submit" class="w-full"> <.button type="submit" class="w-full">
Add Page Add page
</.button> </.button>
</.form> </.form>
</.live_component> </.live_component>

View File

@ -67,7 +67,7 @@ defmodule PlausibleWeb.Live.Sites do
@needs_to_upgrade == {:needs_to_upgrade, :no_active_trial_or_subscription} @needs_to_upgrade == {:needs_to_upgrade, :no_active_trial_or_subscription}
} /> } />
<div class="group mt-6 pb-5 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between"> <div class="group mt-6 pb-5 border-b border-gray-200 dark:border-gray-750 flex items-center justify-between">
<h2 class="text-2xl font-bold leading-7 text-gray-900 dark:text-gray-100 sm:text-3xl sm:leading-9 sm:truncate shrink-0"> <h2 class="text-2xl font-bold leading-7 text-gray-900 dark:text-gray-100 sm:text-3xl sm:leading-9 sm:truncate shrink-0">
{Teams.name(@current_team)} {Teams.name(@current_team)}
<.unstyled_link <.unstyled_link
@ -154,11 +154,11 @@ defmodule PlausibleWeb.Live.Sites do
def upgrade_nag_screen(assigns) do def upgrade_nag_screen(assigns) do
~H""" ~H"""
<div class="rounded-md bg-yellow-100 p-4"> <div class="rounded-md bg-yellow-100 dark:bg-yellow-900/40 p-5">
<div class="flex"> <div class="flex">
<div class="shrink-0"> <div class="shrink-0">
<svg <svg
class="h-5 w-5 text-yellow-400" class="size-5 mt-0.5 text-yellow-500"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20" viewBox="0 0 20 20"
fill="currentColor" fill="currentColor"
@ -171,11 +171,11 @@ defmodule PlausibleWeb.Live.Sites do
/> />
</svg> </svg>
</div> </div>
<div class="ml-3"> <div class="ml-2">
<h3 class="text-sm font-medium text-yellow-800"> <h3 class="font-medium text-gray-900 dark:text-gray-100">
Payment required Payment required
</h3> </h3>
<div class="mt-2 text-sm text-yellow-700"> <div class="mt-1 text-sm text-gray-900/80 dark:text-gray-100/60">
<p> <p>
To access the sites you own, you need to subscribe to a monthly or yearly payment plan. To access the sites you own, you need to subscribe to a monthly or yearly payment plan.
<.styled_link href={Routes.settings_path(PlausibleWeb.Endpoint, :subscription)}> <.styled_link href={Routes.settings_path(PlausibleWeb.Endpoint, :subscription)}>
@ -220,7 +220,7 @@ defmodule PlausibleWeb.Live.Sites do
~H""" ~H"""
<li <li
data-test-id="consolidated-view-card" data-test-id="consolidated-view-card"
class="relative row-span-2 bg-white p-6 dark:bg-gray-800 rounded-md shadow-sm cursor-pointer hover:shadow-lg transition-shadow duration-150" class="relative row-span-2 bg-white p-6 dark:bg-gray-900 rounded-md shadow-sm cursor-pointer hover:shadow-lg transition-shadow duration-150"
> >
<.unstyled_link <.unstyled_link
href={"/#{URI.encode_www_form(@consolidated_view.domain)}"} href={"/#{URI.encode_www_form(@consolidated_view.domain)}"}
@ -281,10 +281,10 @@ defmodule PlausibleWeb.Live.Sites do
class="flex flex-col gap-y-2 min-h-[254px] h-full text-center animate-pulse" class="flex flex-col gap-y-2 min-h-[254px] h-full text-center animate-pulse"
data-test-id="consolidated-viw-stats-loading" data-test-id="consolidated-viw-stats-loading"
> >
<div class="flex-2 dark:bg-gray-700 bg-gray-100 rounded-md"></div> <div class="flex-2 dark:bg-gray-750 bg-gray-100 rounded-md"></div>
<div class="flex-1 flex flex-col gap-y-2"> <div class="flex-1 flex flex-col gap-y-2">
<div class="w-full h-full dark:bg-gray-700 bg-gray-100 rounded-md"></div> <div class="w-full h-full dark:bg-gray-750 bg-gray-100 rounded-md"></div>
<div class="w-full h-full dark:bg-gray-700 bg-gray-100 rounded-md"></div> <div class="w-full h-full dark:bg-gray-750 bg-gray-100 rounded-md"></div>
</div> </div>
</div> </div>
</.unstyled_link> </.unstyled_link>
@ -328,7 +328,7 @@ defmodule PlausibleWeb.Live.Sites do
data-domain={@site.domain} data-domain={@site.domain}
x-on:click={"invitationOpen = true; selectedInvitation = invitations['#{@invitation.invitation_id}']"} x-on:click={"invitationOpen = true; selectedInvitation = invitations['#{@invitation.invitation_id}']"}
> >
<div class="col-span-1 flex flex-col gap-y-5 bg-white dark:bg-gray-800 rounded-md shadow-sm p-6 group-hover:shadow-lg cursor-pointer transition duration-100"> <div class="col-span-1 flex flex-col gap-y-5 bg-white dark:bg-gray-900 rounded-md shadow-sm p-6 group-hover:shadow-lg cursor-pointer transition duration-100">
<div class="w-full flex items-center justify-between gap-x-2.5"> <div class="w-full flex items-center justify-between gap-x-2.5">
<.favicon domain={@site.domain} /> <.favicon domain={@site.domain} />
<div class="flex-1 w-full truncate"> <div class="flex-1 w-full truncate">
@ -369,7 +369,7 @@ defmodule PlausibleWeb.Live.Sites do
} }
> >
<.unstyled_link href={"/#{URI.encode_www_form(@site.domain)}"}> <.unstyled_link href={"/#{URI.encode_www_form(@site.domain)}"}>
<div class="col-span-1 flex flex-col gap-y-5 bg-white dark:bg-gray-800 rounded-md shadow-sm p-6 group-hover:shadow-lg cursor-pointer transition duration-100"> <div class="col-span-1 flex flex-col gap-y-5 bg-white dark:bg-gray-900 rounded-md shadow-sm p-6 group-hover:shadow-lg cursor-pointer transition duration-100">
<div class="w-full flex items-center justify-between gap-x-2.5"> <div class="w-full flex items-center justify-between gap-x-2.5">
<.favicon domain={@site.domain} /> <.favicon domain={@site.domain} />
<div class="flex-1 w-full"> <div class="flex-1 w-full">
@ -479,8 +479,8 @@ defmodule PlausibleWeb.Live.Sites do
"flex flex-col gap-y-2 h-[122px] text-center animate-pulse", "flex flex-col gap-y-2 h-[122px] text-center animate-pulse",
is_map(@hourly_stats) && " hidden" is_map(@hourly_stats) && " hidden"
]}> ]}>
<div class="flex-2 dark:bg-gray-700 bg-gray-100 rounded-md"></div> <div class="flex-2 dark:bg-gray-750 bg-gray-100 rounded-md"></div>
<div class="flex-1 dark:bg-gray-700 bg-gray-100 rounded-md"></div> <div class="flex-1 dark:bg-gray-750 bg-gray-100 rounded-md"></div>
</div> </div>
<div :if={is_map(@hourly_stats)}> <div :if={is_map(@hourly_stats)}>
<span class="flex flex-col gap-y-5 text-gray-600 dark:text-gray-400 text-sm truncate"> <span class="flex flex-col gap-y-5 text-gray-600 dark:text-gray-400 text-sm truncate">
@ -687,7 +687,7 @@ defmodule PlausibleWeb.Live.Sites do
</.button_link> </.button_link>
<.button_link <.button_link
href="#" href="#"
theme="bright" theme="secondary"
data-method="post" data-method="post"
data-csrf={Plug.CSRFProtection.get_csrf_token()} data-csrf={Plug.CSRFProtection.get_csrf_token()}
x-bind:data-to="selectedInvitation && ('/sites/invitations/' + selectedInvitation.invitation.invitation_id + '/reject')" x-bind:data-to="selectedInvitation && ('/sites/invitations/' + selectedInvitation.invitation.invitation_id + '/reject')"

View File

@ -65,7 +65,7 @@ defmodule PlausibleWeb.Live.TeamManagement do
</div> </div>
<.dropdown id="input-role-picker"> <.dropdown id="input-role-picker">
<:button class="role border rounded-sm border-indigo-700 bg-transparent text-gray-800 dark:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-700 focus-visible:outline-gray-100 whitespace-nowrap truncate inline-flex items-center gap-x-2 font-medium rounded-md px-3 py-2 text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:bg-gray-400 dark:disabled:text-white dark:disabled:text-gray-400 dark:disabled:bg-gray-700"> <:button class="role inline-flex items-center gap-x-2 font-medium rounded-md px-3 py-2 text-sm border border-gray-300 dark:border-gray-750 rounded-md text-gray-800 dark:text-gray-100 dark:bg-gray-750 dark:hover:bg-gray-700 focus-visible:outline-gray-100 whitespace-nowrap truncate shadow-xs hover:shadow-sm transition-all duration-150 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:bg-gray-400 dark:disabled:text-white dark:disabled:text-gray-400 dark:disabled:bg-gray-700">
{@input_role |> Atom.to_string() |> String.capitalize()} {@input_role |> Atom.to_string() |> String.capitalize()}
<Heroicons.chevron_down mini class="size-4 mt-0.5" /> <Heroicons.chevron_down mini class="size-4 mt-0.5" />
</:button> </:button>
@ -132,11 +132,11 @@ defmodule PlausibleWeb.Live.TeamManagement do
</div> </div>
<div :if={Layout.has_guests?(@layout)} class="flex items-center mt-4 mb-4" id="guests-hr"> <div :if={Layout.has_guests?(@layout)} class="flex items-center mt-4 mb-4" id="guests-hr">
<hr class="grow border-t border-gray-200 dark:border-gray-600" /> <hr class="grow border-t border-gray-200 dark:border-gray-700" />
<span class="mx-4 text-gray-500 text-sm"> <span class="mx-4 text-gray-500 text-sm">
Guests Guests
</span> </span>
<hr class="grow border-t border-gray-200 dark:border-gray-600" /> <hr class="grow border-t border-gray-200 dark:border-gray-700" />
</div> </div>
<div :if={Layout.has_guests?(@layout)} id="guest-list"> <div :if={Layout.has_guests?(@layout)} id="guest-list">

View File

@ -13,7 +13,7 @@
> >
Log in Log in
</.button_link> </.button_link>
<.button_link theme="bright" href={PlausibleWeb.LayoutView.home_dest(@conn)}> <.button_link theme="secondary" href={PlausibleWeb.LayoutView.home_dest(@conn)}>
Go to homepage Go to homepage
</.button_link> </.button_link>
</div> </div>

View File

@ -1,7 +1,7 @@
<.form <.form
:let={f} :let={f}
for={@conn} for={@conn}
class="max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded-sm px-8 pt-6 pb-8 mb-4 mt-8" class="max-w-md w-full mx-auto bg-white dark:bg-gray-900 shadow-md rounded-sm px-8 pt-6 pb-8 mb-4 mt-8"
onsubmit="confirmButton.disabled = true; return true;" onsubmit="confirmButton.disabled = true; return true;"
action={Routes.google_analytics_path(@conn, :import, @site.domain)} action={Routes.google_analytics_path(@conn, :import, @site.domain)}
> >

View File

@ -1,4 +1,4 @@
<div class="mt-24 bg-gray-800 dark:bg-gray-800"> <div class="mt-24 bg-gray-800 dark:bg-gray-900">
<div class="container px-4 py-12 sm:px-6 lg:py-16 lg:px-8"> <div class="container px-4 py-12 sm:px-6 lg:py-16 lg:px-8">
<div class="xl:grid xl:grid-cols-3 xl:gap-8"> <div class="xl:grid xl:grid-cols-3 xl:gap-8">
<div class="my-8 xl:my-0"> <div class="my-8 xl:my-0">
@ -10,7 +10,7 @@
logo_path("logo_dark.svg") logo_path("logo_dark.svg")
) )
} }
class="inline-block w-40 mr-1" class="inline-block w-32 mr-1"
alt="Plausible logo" alt="Plausible logo"
loading="lazy" loading="lazy"
/> />

View File

@ -30,7 +30,7 @@
<div class="absolute inset-y-0 right-0 flex items-center justify-end"> <div class="absolute inset-y-0 right-0 flex items-center justify-end">
<%= cond do %> <%= cond do %>
<% @conn.assigns[:current_user] -> %> <% @conn.assigns[:current_user] -> %>
<ul class="flex items-center w-full sm:w-auto"> <ul class="flex items-center gap-2 w-full sm:w-auto">
<li :if={ <li :if={
ee?() && @conn.assigns[:site] && ee?() && @conn.assigns[:site] &&
Plausible.Auth.is_super_admin?(@conn.assigns[:current_user]) Plausible.Auth.is_super_admin?(@conn.assigns[:current_user])
@ -45,10 +45,10 @@
</li> </li>
<li <li
:if={ee?() and Plausible.Teams.on_trial?(@conn.assigns[:current_team])} :if={ee?() and Plausible.Teams.on_trial?(@conn.assigns[:current_team])}
class="hidden mr-6 sm:block" class="hidden sm:block"
> >
<.styled_link <.styled_link
class="text-sm text-yellow-900 dark:text-yellow-900 rounded-sm px-3 py-2 rounded-md bg-yellow-100 dark:bg-yellow-100" class="flex items-center h-[40px] px-3 py-2 text-sm text-yellow-700 hover:text-yellow-800 dark:text-yellow-500 dark:hover:text-yellow-500 font-medium rounded-md bg-yellow-100 dark:bg-yellow-800/40 dark:hover:bg-yellow-800/50 transition-colors duration-150"
href={Routes.settings_path(@conn, :subscription)} href={Routes.settings_path(@conn, :subscription)}
> >
{trial_notification(@conn.assigns[:current_team])} {trial_notification(@conn.assigns[:current_team])}
@ -56,13 +56,13 @@
</li> </li>
<li class="w-full sm:w-auto"> <li class="w-full sm:w-auto">
<.dropdown> <.dropdown>
<:button class="flex items-center gap-3 px-3 py-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800"> <:button class="flex items-center gap-2 pl-3 pr-1.5 py-2 rounded-md transition-colors duration-150 hover:bg-gray-100 dark:hover:bg-gray-800">
<span class="font-medium truncate dark:text-gray-100 hidden md:block"> <span class="hidden md:block text-sm font-medium truncate dark:text-gray-100">
{@conn.assigns[:current_user].name} {@conn.assigns[:current_user].name}
</span> </span>
<img <img
src={Plausible.Auth.User.profile_img_url(@conn.assigns[:current_user])} src={Plausible.Auth.User.profile_img_url(@conn.assigns[:current_user])}
class="w-7 rounded-full" class="-my-0.5 w-7 rounded-full"
/> />
</:button> </:button>
<:menu> <:menu>

View File

@ -26,7 +26,7 @@
</head> </head>
<body <body
class={[ class={[
"flex flex-col bg-gray-50 dark:bg-gray-850", "flex flex-col bg-gray-50 dark:bg-gray-950",
if !assigns[:embedded] do if !assigns[:embedded] do
"h-full" "h-full"
end end

View File

@ -3,7 +3,7 @@
<.styled_link class="font-semibold text-sm" href="/sites"> <.styled_link class="font-semibold text-sm" href="/sites">
← Back to sites ← Back to sites
</.styled_link> </.styled_link>
<div class="pb-5 border-b border-gray-200 dark:border-gray-700"> <div class="pb-5 border-b border-gray-200 dark:border-gray-750">
<h2 class="text-2xl font-bold leading-7 text-gray-900 dark:text-gray-100 sm:text-3xl sm:leading-9 sm:truncate"> <h2 class="text-2xl font-bold leading-7 text-gray-900 dark:text-gray-100 sm:text-3xl sm:leading-9 sm:truncate">
Settings Settings
</h2> </h2>

View File

@ -6,7 +6,7 @@
> >
← Back to stats ← Back to stats
</.styled_link> </.styled_link>
<div class="pb-5 border-b border-gray-200 dark:border-gray-700"> <div class="pb-5 border-b border-gray-200 dark:border-gray-750">
<h2 class="text-2xl font-bold leading-7 text-gray-900 dark:text-gray-100 sm:text-3xl sm:leading-9 sm:truncate"> <h2 class="text-2xl font-bold leading-7 text-gray-900 dark:text-gray-100 sm:text-3xl sm:leading-9 sm:truncate">
Settings for {@site.domain} Settings for {@site.domain}
</h2> </h2>

View File

@ -43,7 +43,12 @@
</:tbody> </:tbody>
<tr :if={length(invoice_list) > 12}> <tr :if={length(invoice_list) > 12}>
<td colspan="3" class="text-center pt-8 pb-4"> <td colspan="3" class="text-center pt-8 pb-4">
<.button_link href={} theme="bright" x-on:click="showAll = true" x-show="!showAll"> <.button_link
href={}
theme="secondary"
x-on:click="showAll = true"
x-show="!showAll"
>
Show more Show more
</.button_link> </.button_link>
</td> </td>

View File

@ -33,7 +33,7 @@
team={@current_team} team={@current_team}
subscription={@subscription} subscription={@subscription}
/> />
<div class="w-full flex-1 h-32 px-2 py-4 text-center bg-gray-100 rounded-sm dark:bg-gray-900"> <div class="w-full flex-1 h-32 px-2 py-4 text-center bg-gray-100 rounded-sm dark:bg-gray-800">
<h4 class="font-black dark:text-gray-100">Next bill amount</h4> <h4 class="font-black dark:text-gray-100">Next bill amount</h4>
<%= if Plausible.Billing.Subscription.Status.in?(@subscription, [Plausible.Billing.Subscription.Status.active(), Plausible.Billing.Subscription.Status.past_due()]) do %> <%= if Plausible.Billing.Subscription.Status.in?(@subscription, [Plausible.Billing.Subscription.Status.active(), Plausible.Billing.Subscription.Status.past_due()]) do %>
<div class="py-2 text-xl font-medium dark:text-gray-100"> <div class="py-2 text-xl font-medium dark:text-gray-100">
@ -46,7 +46,7 @@
<div class="py-2 text-xl font-medium dark:text-gray-100">---</div> <div class="py-2 text-xl font-medium dark:text-gray-100">---</div>
<% end %> <% end %>
</div> </div>
<div class="w-full flex-1 h-32 px-2 py-4 text-center bg-gray-100 rounded-sm dark:bg-gray-900"> <div class="w-full flex-1 h-32 px-2 py-4 text-center bg-gray-100 rounded-sm dark:bg-gray-800">
<h4 class="font-black dark:text-gray-100">Next bill date</h4> <h4 class="font-black dark:text-gray-100">Next bill date</h4>
<%= if @subscription && @subscription.next_bill_date && Plausible.Billing.Subscription.Status.in?(@subscription, [Plausible.Billing.Subscription.Status.active(), Plausible.Billing.Subscription.Status.past_due()]) do %> <%= if @subscription && @subscription.next_bill_date && Plausible.Billing.Subscription.Status.in?(@subscription, [Plausible.Billing.Subscription.Status.active(), Plausible.Billing.Subscription.Status.past_due()]) do %>

View File

@ -4,8 +4,9 @@
</:title> </:title>
<:subtitle> <:subtitle>
Enter the email address and role of the person you want to invite. We will contact them over email to offer them access to {@site.domain} analytics.<br /><br /> Enter their email and role, and we'll email them an invite to join
The invitation will expire in 48 hours <span class="font-bold">{@site.domain}</span>
analytics. Invitations expire after 48 hours.
</:subtitle> </:subtitle>
<.form :let={f} for={@conn} action={Routes.membership_path(@conn, :invite_member, @site.domain)}> <.form :let={f} for={@conn} action={Routes.membership_path(@conn, :invite_member, @site.domain)}>
@ -31,13 +32,13 @@
<% end %> <% end %>
</div> </div>
<fieldset x-data="{selectedOption: null}"> <fieldset x-data="{selectedOption: null}" class="flex flex-col gap-y-4">
<.label for={f[:role].id}> <.label for={f[:role].id}>
Role Role
</.label> </.label>
<div class="mt-1 bg-white rounded-md -space-y-px dark:bg-gray-800"> <div class="flex flex-col gap-y-4">
<label <label
class="border-gray-200 dark:border-gray-500 rounded-tl-md rounded-tr-md relative border p-4 flex cursor-pointer" class="relative flex cursor-pointer"
x-class="{'bg-indigo-50 border-indigo-200 dark:bg-indigo-500 dark:border-indigo-800 z-10': selectedOption === 'editor', 'border-gray-200': selectedOption !== 'editor'}" x-class="{'bg-indigo-50 border-indigo-200 dark:bg-indigo-500 dark:border-indigo-800 z-10': selectedOption === 'editor', 'border-gray-200': selectedOption !== 'editor'}"
> >
<.input <.input
@ -67,7 +68,7 @@
</label> </label>
<label <label
class="border-gray-200 dark:border-gray-500 rounded-bl-md rounded-br-md relative border p-4 flex cursor-pointer" class="relative flex cursor-pointer"
x-class="{'bg-indigo-50 border-indigo-200 dark:bg-indigo-500 dark:border-indigo-800 z-10': selectedOption === 'viewer', 'border-gray-200': selectedOption !== 'viewer'}" x-class="{'bg-indigo-50 border-indigo-200 dark:bg-indigo-500 dark:border-indigo-800 z-10': selectedOption === 'viewer', 'border-gray-200': selectedOption !== 'viewer'}"
> >
<.input <.input

View File

@ -32,7 +32,7 @@
id="tz-select" id="tz-select"
value="Etc/Greenwich" value="Etc/Greenwich"
disabled={@site_limit_exceeded?} disabled={@site_limit_exceeded?}
label="Reporting Timezone" label="Reporting timezone"
options={Plausible.Timezones.options()} options={Plausible.Timezones.options()}
/> />
</div> </div>

View File

@ -1,5 +1,5 @@
<.focus_box> <.focus_box>
<:title>New Shared Link</:title> <:title>New shared link</:title>
<:subtitle> <:subtitle>
Password protection is optional. Please make sure you save it in a secure place. Once the link is created, we cannot reveal the password. Password protection is optional. Please make sure you save it in a secure place. Once the link is created, we cannot reveal the password.
</:subtitle> </:subtitle>

View File

@ -175,7 +175,7 @@
<:tbody :let={recipient}> <:tbody :let={recipient}>
<.td> <.td>
<div class="flex items-cetner gap-x-2"> <div class="flex items-center gap-x-2">
<Heroicons.envelope_open class="w-6 h-6 feather" /> <Heroicons.envelope_open class="w-6 h-6 feather" />
<div> <div>
{recipient} {recipient}

View File

@ -19,7 +19,7 @@
<.form :let={f} for={@changeset} action={"/#{URI.encode_www_form(@site.domain)}/settings"}> <.form :let={f} for={@changeset} action={"/#{URI.encode_www_form(@site.domain)}/settings"}>
<.input <.input
field={f[:timezone]} field={f[:timezone]}
label="Reporting Timezone" label="Reporting timezone"
type="select" type="select"
options={Plausible.Timezones.options()} options={Plausible.Timezones.options()}
width="w-1/2" width="w-1/2"

View File

@ -69,7 +69,7 @@
<.button type="submit">Save</.button> <.button type="submit">Save</.button>
</.form> </.form>
<% {:error, error} -> %> <% {:error, error} -> %>
<.notice title="Integration Error" theme={:red} class="mt-8"> <.notice title="Integration error" theme={:red} class="mt-8">
<%= case error do %> <%= case error do %>
<% "invalid_grant" -> %> <% "invalid_grant" -> %>
Invalid Grant error returned from Google. Invalid Grant error returned from Google.

View File

@ -29,7 +29,7 @@
} /> } />
<div class="flow-root"> <div class="flow-root">
<ul class="divide-y divide-gray-200 dark:divide-gray-600"> <ul class="divide-y divide-gray-200 dark:divide-gray-700">
<%= for membership <- @memberships do %> <%= for membership <- @memberships do %>
<li class="py-4" id={"membership-#{membership.user.id}"}> <li class="py-4" id={"membership-#{membership.user.id}"}>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
@ -52,7 +52,7 @@
</div> </div>
<.dropdown> <.dropdown>
<:button class="bg-transparent text-gray-800 dark:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-700 focus-visible:outline-gray-100 whitespace-nowrap truncate inline-flex items-center gap-x-2 font-medium rounded-md px-3.5 py-2.5 text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:bg-gray-400 dark:disabled:text-white dark:disabled:text-gray-400 dark:disabled:bg-gray-700"> <:button class="bg-transparent text-gray-800 dark:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-800 focus-visible:outline-gray-100 whitespace-nowrap truncate inline-flex items-center gap-x-2 font-medium rounded-md px-3.5 py-2.5 text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:bg-gray-400 dark:disabled:text-white dark:disabled:text-gray-400 dark:disabled:bg-gray-700">
{site_role(membership)} {site_role(membership)}
<Heroicons.chevron_down mini class="size-4 mt-0.5" /> <Heroicons.chevron_down mini class="size-4 mt-0.5" />
</:button> </:button>
@ -111,7 +111,7 @@
membership.user.id membership.user.id
) )
} }
class="text-red-600 hover:text-red-600" class="!text-red-600 hover:text-red-600 dark:!text-red-500 dark:hover:!text-red-400"
method="delete" method="delete"
disabled={@site_role not in [:owner, :admin]} disabled={@site_role not in [:owner, :admin]}
> >

View File

@ -9,10 +9,10 @@
conn={@conn} conn={@conn}
> >
<:title> <:title>
Custom Properties Custom properties
</:title> </:title>
<:subtitle> <:subtitle>
Attach Custom Properties when sending a Pageview or an Event to Attach custom properties when sending a pageview or an event to
create custom metrics. create custom metrics.
</:subtitle> </:subtitle>

View File

@ -144,14 +144,14 @@
rows="6" rows="6"
readonly="readonly" readonly="readonly"
onclick="this.select()" onclick="this.select()"
class="block w-full border-gray-300 dark:border-gray-700 resize-none text-sm shadow-xs focus:ring-indigo-500 focus:border-indigo-500 rounded-md dark:bg-gray-900 dark:text-gray-300" class="block w-full border-gray-300 dark:border-gray-750 resize-none text-sm shadow-xs focus:ring-indigo-500 focus:border-indigo-500 rounded-md dark:bg-gray-750 dark:text-gray-300"
></textarea> ></textarea>
<a <a
onclick="var textarea = document.getElementById('embed-code'); textarea.focus(); textarea.select(); document.execCommand('copy');" onclick="var textarea = document.getElementById('embed-code'); textarea.focus(); textarea.select(); document.execCommand('copy');"
href="javascript:void(0)" href="javascript:void(0)"
class="text-sm text-indigo-500 no-underline hover:underline" class="text-sm text-indigo-500 no-underline hover:underline"
> >
<Heroicons.document_duplicate class="h-5 w-5 absolute text-indigo-700 top-3 right-3" /> <Heroicons.document_duplicate class="size-5 absolute text-indigo-600 hover:text-indigo-700 dark:text-indigo-500 dark:hover:text-indigo-400 top-3 right-3 transition-colors duration-150" />
</a> </a>
</div> </div>
</.tile> </.tile>

View File

@ -1,6 +1,6 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21 11H1" stroke="#90A1B9" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/> <path d="M21 11H1" stroke="#a1a1aa" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 21C16.7143 15.5578 16.7143 6.44218 11 1" stroke="#90A1B9" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/> <path d="M11 21C16.7143 15.5578 16.7143 6.44218 11 1" stroke="#a1a1aa" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.0006 21C5.28627 15.5578 5.28627 6.44218 11.0006 1" stroke="#90A1B9" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/> <path d="M11.0006 21C5.28627 15.5578 5.28627 6.44218 11.0006 1" stroke="#a1a1aa" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 21C16.5228 21 21 16.5228 21 11C21 5.47715 16.5228 1 11 1C5.47715 1 1 5.47715 1 11C1 16.5228 5.47715 21 11 21Z" stroke="#90A1B9" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/> <path d="M11 21C16.5228 21 21 16.5228 21 11C21 5.47715 16.5228 1 11 1C5.47715 1 1 5.47715 1 11C1 16.5228 5.47715 21 11 21Z" stroke="#a1a1aa" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 719 B

After

Width:  |  Height:  |  Size: 719 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#64748b"><path d="M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"></path><path d="M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#71717a"><path d="M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"></path><path d="M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"></path></svg>

Before

Width:  |  Height:  |  Size: 496 B

After

Width:  |  Height:  |  Size: 496 B

View File

@ -23,10 +23,10 @@ defmodule PlausibleWeb.Storybook.Button do
slots: ["Click me!"] slots: ["Click me!"]
}, },
%Variation{ %Variation{
id: :bright, id: :secondary,
description: "Bright button", description: "Secondary button",
attributes: %{ attributes: %{
"theme" => "bright" "theme" => "secondary"
}, },
slots: ["Click me!"] slots: ["Click me!"]
}, },

View File

@ -1605,7 +1605,7 @@ defmodule PlausibleWeb.SiteControllerTest do
test "shows form for new shared link", %{conn: conn, site: site} do test "shows form for new shared link", %{conn: conn, site: site} do
conn = get(conn, "/sites/#{site.domain}/shared-links/new") conn = get(conn, "/sites/#{site.domain}/shared-links/new")
assert html_response(conn, 200) =~ "New Shared Link" assert html_response(conn, 200) =~ "New shared link"
end end
end end

View File

@ -16,10 +16,10 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
assert element_exists?(html, ~s/a#event-tab/) assert element_exists?(html, ~s/a#event-tab/)
pageview_tab = lv |> element(~s/a#pageview-tab/) |> render_click() pageview_tab = lv |> element(~s/a#pageview-tab/) |> render_click()
assert pageview_tab =~ "Page Path" assert pageview_tab =~ "Page path"
event_tab = lv |> element(~s/a#event-tab/) |> render_click() event_tab = lv |> element(~s/a#event-tab/) |> render_click()
assert event_tab =~ "Event Name" assert event_tab =~ "Event name"
end end
test "can navigate to scroll tab if scroll_depth feature visible for site/user", test "can navigate to scroll tab if scroll_depth feature visible for site/user",

View File

@ -46,7 +46,7 @@ defmodule PlausibleWeb.Live.PropsSettingsTest do
conn = get(conn, "/#{site.domain}/settings/properties") conn = get(conn, "/#{site.domain}/settings/properties")
resp = html_response(conn, 200) resp = html_response(conn, 200)
assert resp =~ "Attach Custom Properties" assert resp =~ "Attach custom properties"
assert element_exists?( assert element_exists?(
resp, resp,
@ -182,7 +182,7 @@ defmodule PlausibleWeb.Live.PropsSettingsTest do
lv = get_liveview(conn, site) lv = get_liveview(conn, site)
html = lv |> element(~s/button[phx-click="add-prop"]/) |> render_click() html = lv |> element(~s/button[phx-click="add-prop"]/) |> render_click()
assert html =~ "Add Property for #{site.domain}" assert html =~ "Add property for #{site.domain}"
assert element_exists?( assert element_exists?(
html, html,