@@ -75,7 +75,7 @@ export default class OperatingSystems extends React.Component {
)
} else {
- return
}
}
diff --git a/assets/js/dashboard/stats/modals/countries.js b/assets/js/dashboard/stats/modals/countries.js
index 85a196a8fd2e..82da8faa2006 100644
--- a/assets/js/dashboard/stats/modals/countries.js
+++ b/assets/js/dashboard/stats/modals/countries.js
@@ -26,7 +26,7 @@ class CountriesModal extends React.Component {
query.set('country', country.name)
return (
-
{country.full_country_name}
@@ -51,15 +51,15 @@ class CountriesModal extends React.Component {
} else if (this.state.countries) {
return (
- Top countries
+ Top countries
-
+
- Country |
- {this.label()} |
+ Country |
+ {this.label()} |
diff --git a/assets/js/dashboard/stats/modals/google-keywords.js b/assets/js/dashboard/stats/modals/google-keywords.js
index 2068d1aa8c4f..a26e7cefa577 100644
--- a/assets/js/dashboard/stats/modals/google-keywords.js
+++ b/assets/js/dashboard/stats/modals/google-keywords.js
@@ -43,7 +43,7 @@ class GoogleKeywordsModal extends React.Component {
return (
-
+
{term.name} |
{numberFormatter(term.count)} |
@@ -54,7 +54,7 @@ class GoogleKeywordsModal extends React.Component {
renderKeywords() {
if (this.state.query.filters.goal) {
return (
-
+
Sorry, we cannot show which keywords converted best for goal {this.state.query.filters.goal}
Google does not share this information
@@ -63,7 +63,7 @@ class GoogleKeywordsModal extends React.Component {
} else if (this.state.notConfigured) {
if (this.state.isOwner) {
return (
-
+
The site is not connected to Google Search Keywords
Configure the integration to view search terms
@@ -72,7 +72,7 @@ class GoogleKeywordsModal extends React.Component {
)
} else {
return (
-
+
The site is not connected to Google Search Kewyords
Cannot show search terms
@@ -84,8 +84,8 @@ class GoogleKeywordsModal extends React.Component {
- Search Term |
- Visitors |
+ Search Term |
+ Visitors |
@@ -95,7 +95,7 @@ class GoogleKeywordsModal extends React.Component {
)
} else {
return (
-
+
Could not find any search terms for this period
@@ -106,7 +106,7 @@ class GoogleKeywordsModal extends React.Component {
renderGoalText() {
if (this.state.query.filters.goal) {
return (
- completed {this.state.query.filters.goal}
+ completed {this.state.query.filters.goal}
)
}
}
@@ -119,11 +119,11 @@ class GoogleKeywordsModal extends React.Component {
} else {
return (
- ← All referrers
+ ← All referrers
-
+
-
+
{this.state.totalVisitors} visitors from Google
{toHuman(this.state.query)}
diff --git a/assets/js/dashboard/stats/modals/modal.js b/assets/js/dashboard/stats/modals/modal.js
index 505393e5c1ff..2e33290c0996 100644
--- a/assets/js/dashboard/stats/modals/modal.js
+++ b/assets/js/dashboard/stats/modals/modal.js
@@ -47,7 +47,7 @@ class Modal extends React.Component {
-
+
{this.props.children}
diff --git a/assets/js/dashboard/stats/modals/pages.js b/assets/js/dashboard/stats/modals/pages.js
index c073a2b2c04c..3850995d78fb 100644
--- a/assets/js/dashboard/stats/modals/pages.js
+++ b/assets/js/dashboard/stats/modals/pages.js
@@ -51,7 +51,7 @@ class PagesModal extends React.Component {
query.set('page', page.name)
return (
-
+
{page.name}
|
@@ -79,17 +79,17 @@ class PagesModal extends React.Component {
} else if (this.state.pages) {
return (
- {this.title()}
+ {this.title()}
- Page url |
- { this.label() } |
- {this.showPageviews() && Pageviews | }
- {this.showBounceRate() && Bounce rate | }
+ Page url |
+ { this.label() } |
+ {this.showPageviews() && Pageviews | }
+ {this.showBounceRate() && Bounce rate | }
diff --git a/assets/js/dashboard/stats/modals/referrer-drilldown.js b/assets/js/dashboard/stats/modals/referrer-drilldown.js
index 4dc2c9060d3e..50b20608d62a 100644
--- a/assets/js/dashboard/stats/modals/referrer-drilldown.js
+++ b/assets/js/dashboard/stats/modals/referrer-drilldown.js
@@ -47,7 +47,7 @@ class ReferrerDrilldownModal extends React.Component {
if (name !== 'Direct / None') {
return (
-
+
)
}
@@ -60,7 +60,7 @@ class ReferrerDrilldownModal extends React.Component {
return (
-
+
{referrer.name}
{ this.renderExternalLink(name) }
@@ -71,7 +71,7 @@ class ReferrerDrilldownModal extends React.Component {
renderTweet(tweet, index) {
const authorUrl = `https://twitter.com/${tweet.author_handle}`
const tweetUrl = `${authorUrl}/status/${tweet.tweet_id}`
- const border = index === 0 ? '' : ' pt-4 border-t border-gray-300'
+ const border = index === 0 ? '' : ' pt-4 border-t border-gray-300 dark:border-gray-500'
return (
@@ -80,14 +80,14 @@ class ReferrerDrilldownModal extends React.Component {
{tweet.author_name}
- @{tweet.author_handle}
+ @{tweet.author_handle}
-
+
{formatFullDate(new Date(tweet.created))}
@@ -97,13 +97,13 @@ class ReferrerDrilldownModal extends React.Component {
renderReferrer(referrer) {
if (referrer.tweets) {
return (
-
+
{ this.renderReferrerName(referrer) }
appears in {referrer.tweets.length} tweets
-
+
{ referrer.tweets.map(this.renderTweet) }
|
@@ -114,7 +114,7 @@ class ReferrerDrilldownModal extends React.Component {
)
} else {
return (
-
+
{ this.renderReferrerName(referrer) }
|
@@ -129,7 +129,7 @@ class ReferrerDrilldownModal extends React.Component {
renderGoalText() {
if (this.state.query.filters.goal) {
return (
- completed {this.state.query.filters.goal}
+ completed {this.state.query.filters.goal}
)
}
}
@@ -142,20 +142,20 @@ class ReferrerDrilldownModal extends React.Component {
} else if (this.state.referrers) {
return (
- Referrer drilldown
+ Referrer drilldown
-
+
- {this.state.totalVisitors} visitors from {decodeURIComponent(this.props.match.params.referrer)} {toHuman(this.state.query)}
+ {this.state.totalVisitors} visitors from {decodeURIComponent(this.props.match.params.referrer)} {toHuman(this.state.query)}
{this.renderGoalText()}
- Referrer |
- Visitors |
- {this.showExtra() && Bounce rate | }
- {this.showExtra() && Visit duration | }
+ Referrer |
+ Visitors |
+ {this.showExtra() && Bounce rate | }
+ {this.showExtra() && Visit duration | }
diff --git a/assets/js/dashboard/stats/modals/sources.js b/assets/js/dashboard/stats/modals/sources.js
index e2b792e6b252..ee41a3b039ba 100644
--- a/assets/js/dashboard/stats/modals/sources.js
+++ b/assets/js/dashboard/stats/modals/sources.js
@@ -84,7 +84,7 @@ class SourcesModal extends React.Component {
if (filter === 'utm_campaigns') query.set('utm_campaign', source.name)
return (
-
+
{ source.name }
@@ -121,18 +121,18 @@ class SourcesModal extends React.Component {
render() {
return (
- {this.title()}
+ {this.title()}
-
+
- Source |
- {this.label()} |
- {this.showExtra() && Bounce rate | }
- {this.showExtra() && Visit duration | }
+ Source |
+ {this.label()} |
+ {this.showExtra() && Bounce rate | }
+ {this.showExtra() && Visit duration | }
diff --git a/assets/js/dashboard/stats/more-link.js b/assets/js/dashboard/stats/more-link.js
index 46a46a735fa7..54d4135d134b 100644
--- a/assets/js/dashboard/stats/more-link.js
+++ b/assets/js/dashboard/stats/more-link.js
@@ -5,7 +5,7 @@ export default function MoreLink({site, list, endpoint}) {
if (list.length > 0) {
return (
-
+
MORE
diff --git a/assets/js/dashboard/stats/pages.js b/assets/js/dashboard/stats/pages.js
index c14745439c57..20fd43e85197 100644
--- a/assets/js/dashboard/stats/pages.js
+++ b/assets/js/dashboard/stats/pages.js
@@ -45,15 +45,15 @@ export default class Pages extends React.Component {
return (
- {numberFormatter(page.count)}
+ {numberFormatter(page.count)}
)
}
@@ -73,7 +73,7 @@ export default class Pages extends React.Component {
if (this.state.pages.length > 0) {
return (
-
+
Page url
{ this.label() }
@@ -84,7 +84,7 @@ export default class Pages extends React.Component {
)
} else {
- return No data yet
+ return No data yet
}
}
@@ -97,7 +97,7 @@ export default class Pages extends React.Component {
if (this.state.pages) {
return (
- {this.title()}
+ {this.title()}
{ this.renderList() }
@@ -107,7 +107,7 @@ export default class Pages extends React.Component {
render() {
return (
-
+
{ this.state.loading && }
{ this.renderContent() }
diff --git a/assets/js/dashboard/stats/sources/referrer-list.js b/assets/js/dashboard/stats/sources/referrer-list.js
index 3c802b54aae1..2b6e38744f44 100644
--- a/assets/js/dashboard/stats/sources/referrer-list.js
+++ b/assets/js/dashboard/stats/sources/referrer-list.js
@@ -57,7 +57,7 @@ export default class Referrers extends React.Component {
if (this.props.query.filters.source && this.props.query.filters.source !== 'Google' && referrer.name !== 'Direct / None') {
return (
-
+
)
}
@@ -71,16 +71,16 @@ export default class Referrers extends React.Component {
return (
-
+
-
+
{ referrer.name }
{ this.renderExternalLink(referrer) }
- {numberFormatter(referrer.count)}
+ {numberFormatter(referrer.count)}
)
}
@@ -93,7 +93,7 @@ export default class Referrers extends React.Component {
if (this.state.referrers.length > 0) {
return (
-
+
Referrer
{ this.label() }
@@ -104,7 +104,7 @@ export default class Referrers extends React.Component {
)
} else {
- return No data yet
+ return No data yet
}
}
@@ -112,7 +112,7 @@ export default class Referrers extends React.Component {
if (this.state.referrers) {
return (
- Top Referrers
+ Top Referrers
{ this.renderList() }
@@ -122,7 +122,7 @@ export default class Referrers extends React.Component {
render() {
return (
-
+
{ this.state.loading && }
{ this.renderContent() }
diff --git a/assets/js/dashboard/stats/sources/search-terms.js b/assets/js/dashboard/stats/sources/search-terms.js
index 97db8ed95e30..6e0f0e378ec5 100644
--- a/assets/js/dashboard/stats/sources/search-terms.js
+++ b/assets/js/dashboard/stats/sources/search-terms.js
@@ -37,14 +37,14 @@ export default class SearchTerms extends React.Component {
return (
-
-
+
+
{ term.name }
- {numberFormatter(term.count)}
+ {numberFormatter(term.count)}
)
}
@@ -52,7 +52,7 @@ export default class SearchTerms extends React.Component {
renderList() {
if (this.props.query.filters.goal) {
return (
-
+
Sorry, we cannot show which keywords converted best for goal {this.props.query.filters.goal}
Google does not share this information
@@ -61,7 +61,7 @@ export default class SearchTerms extends React.Component {
} else if (this.state.notConfigured) {
return (
-
+
The site is not connected to Google Search Keywords
Cannot show search terms
@@ -73,7 +73,7 @@ export default class SearchTerms extends React.Component {
return (
-
+
Search term
{valLabel}
@@ -83,7 +83,7 @@ export default class SearchTerms extends React.Component {
)
} else {
return (
-
+
Could not find any search terms for this period
Google Search Console data is sampled and delayed by 24-36h
@@ -97,7 +97,7 @@ export default class SearchTerms extends React.Component {
if (this.state.searchTerms) {
return (
- Search Terms
+ Search Terms
{ this.renderList() }
@@ -107,7 +107,7 @@ export default class SearchTerms extends React.Component {
render() {
return (
-
+
{ this.state.loading && }
{ this.renderContent() }
diff --git a/assets/js/dashboard/stats/sources/source-list.js b/assets/js/dashboard/stats/sources/source-list.js
index 38f65db639b1..08745b9e2dcb 100644
--- a/assets/js/dashboard/stats/sources/source-list.js
+++ b/assets/js/dashboard/stats/sources/source-list.js
@@ -42,15 +42,15 @@ class AllSources extends React.Component {
return (
-
-
+
+
{ referrer.name }
- {numberFormatter(referrer.count)}
+ {numberFormatter(referrer.count)}
)
}
@@ -83,7 +83,7 @@ class AllSources extends React.Component {
return (
- Top sources
+ Top sources
{ this.props.renderTabs() }
{ this.renderList() }
@@ -95,7 +95,7 @@ class AllSources extends React.Component {
render() {
return (
-
+
{ this.state.loading && }
{ this.renderContent() }
@@ -146,14 +146,14 @@ class UTMSources extends React.Component {
return (
-
-
+
+
{ referrer.name }
- {numberFormatter(referrer.count)}
+ {numberFormatter(referrer.count)}
)
}
@@ -166,7 +166,7 @@ class UTMSources extends React.Component {
if (this.state.referrers.length > 0) {
return (
-
+
{UTM_TAGS[this.props.tab].label}
{this.label()}
@@ -177,7 +177,7 @@ class UTMSources extends React.Component {
)
} else {
- return No data yet
+ return No data yet
}
}
@@ -186,7 +186,7 @@ class UTMSources extends React.Component {
return (
- Top sources
+ Top sources
{ this.props.renderTabs() }
{ this.renderList() }
@@ -198,7 +198,7 @@ class UTMSources extends React.Component {
render() {
return (
-
+
{ this.state.loading && }
{ this.renderContent() }
@@ -226,10 +226,10 @@ export default class SourceList extends React.Component {
}
renderTabs() {
- const activeClass = 'inline-block h-5 text-indigo-700 font-bold border-b-2 border-indigo-700'
- const defaultClass = 'hover:text-indigo-700 cursor-pointer'
+ const activeClass = 'inline-block h-5 text-indigo-700 dark:text-indigo-500 font-bold border-b-2 border-indigo-700 dark:border-indigo-500'
+ const defaultClass = 'hover:text-indigo-600 cursor-pointer'
return (
-
+
- All
- Medium
- Source
diff --git a/assets/js/dashboard/stats/visitor-graph.js b/assets/js/dashboard/stats/visitor-graph.js
index 6744c5ceeef6..b61d78d33794 100644
--- a/assets/js/dashboard/stats/visitor-graph.js
+++ b/assets/js/dashboard/stats/visitor-graph.js
@@ -4,6 +4,7 @@ import Chart from 'chart.js'
import { eventName, navigateToQuery } from '../query'
import numberFormatter, {durationFormatter} from '../number-formatter'
import * as api from '../api'
+import {ThemeContext} from '../theme-context'
function buildDataSet(plot, present_index, ctx, label) {
var gradient = ctx.createLinearGradient(0, 0, 0, 300);
@@ -92,13 +93,18 @@ function dateFormatter(interval, longForm) {
}
class LineGraph extends React.Component {
- componentDidMount() {
+ constructor(props) {
+ super(props);
+ this.regenerateChart = this.regenerateChart.bind(this);
+ }
+
+ regenerateChart() {
const {graphData} = this.props
this.ctx = document.getElementById("main-graph-canvas").getContext('2d');
const label = this.props.query.filters.goal ? 'Converted visitors' : graphData.interval === 'minute' ? 'Pageviews' : 'Visitors'
const dataSet = buildDataSet(graphData.plot, graphData.present_index, this.ctx, label)
- this.chart = new Chart(this.ctx, {
+ return new Chart(this.ctx, {
type: 'line',
data: {
labels: graphData.labels,
@@ -157,6 +163,7 @@ class LineGraph extends React.Component {
beginAtZero: true,
autoSkip: true,
maxTicksLimit: 8,
+ fontColor: this.props.darkTheme ? 'rgb(243, 244, 246)' : undefined
},
gridLines: {
zeroLineColor: 'transparent',
@@ -171,6 +178,7 @@ class LineGraph extends React.Component {
autoSkip: true,
maxTicksLimit: 8,
callback: dateFormatter(graphData.interval),
+ fontColor: this.props.darkTheme ? 'rgb(243, 244, 246)' : undefined
}
}]
}
@@ -178,6 +186,10 @@ class LineGraph extends React.Component {
});
}
+ componentDidMount() {
+ this.chart = this.regenerateChart();
+ }
+
componentDidUpdate(prevProps) {
if (this.props.graphData !== prevProps.graphData) {
const label = this.props.query.filters.goal ? 'Converted visitors' : this.props.graphData.interval === 'minute' ? 'Pageviews' : 'Visitors'
@@ -189,6 +201,11 @@ class LineGraph extends React.Component {
this.chart.update()
}
+
+ if (prevProps.darkTheme !== this.props.darkTheme) {
+ this.chart = this.regenerateChart();
+ this.chart.update();
+ }
}
onClick(e) {
@@ -220,12 +237,12 @@ class LineGraph extends React.Component {
if (comparison > 0) {
const color = name === 'Bounce rate' ? 'text-red-400' : 'text-green-500'
- return ↑ {formattedComparison}%
+ return ↑ {formattedComparison}%
} else if (comparison < 0) {
const color = name === 'Bounce rate' ? 'text-green-500' : 'text-red-400'
- return ↓ {formattedComparison}%
+ return ↓ {formattedComparison}%
} else if (comparison === 0) {
- return 〰 N/A
+ return 〰 N/A
}
}
@@ -247,9 +264,9 @@ class LineGraph extends React.Component {
return (
- {stat.name}
+ {stat.name}
- { this.renderTopStatNumber(stat) }
+ { this.renderTopStatNumber(stat) }
{this.renderComparison(stat.name, stat.change)}
@@ -268,7 +285,7 @@ class LineGraph extends React.Component {
return (
-
+
)
}
@@ -321,14 +338,18 @@ export default class VisitorGraph extends React.Component {
renderInner() {
if (this.state.graphData) {
return (
-
+
+ {theme => (
+
+ )}
+
)
}
}
render() {
return (
-
+
{ this.state.loading && }
{ this.renderInner() }
diff --git a/assets/js/dashboard/theme-consumer-hoc.js b/assets/js/dashboard/theme-consumer-hoc.js
new file mode 100644
index 000000000000..a52755c51497
--- /dev/null
+++ b/assets/js/dashboard/theme-consumer-hoc.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { ThemeContext } from './theme-context'
+
+export const withThemeConsumer = (WrappedComponent) => {
+ return class extends React.Component {
+ render() {
+ return (
+
+ {theme => (
+
+ )}
+
+ );
+ }
+ }
+}
diff --git a/assets/js/dashboard/theme-context.js b/assets/js/dashboard/theme-context.js
new file mode 100644
index 000000000000..e7f9319d43c2
--- /dev/null
+++ b/assets/js/dashboard/theme-context.js
@@ -0,0 +1,5 @@
+import React from 'react';
+
+export const ThemeContext = React.createContext({
+ dark: false
+});
diff --git a/assets/js/dashboard/theme-provider-hoc.js b/assets/js/dashboard/theme-provider-hoc.js
new file mode 100644
index 000000000000..df4a9c0f321f
--- /dev/null
+++ b/assets/js/dashboard/theme-provider-hoc.js
@@ -0,0 +1,37 @@
+import React from 'react';
+import { ThemeContext } from './theme-context'
+
+export const withThemeProvider = (WrappedComponent) => {
+ return class extends React.Component {
+ constructor(props) {
+ super(props)
+ this.state = {
+ dark: document.querySelector('html').classList.contains('dark') || false
+ };
+
+ this.mutationObserver = new MutationObserver((mutationsList, observer) => {
+ mutationsList.forEach(mutation => {
+ if (mutation.attributeName === 'class') {
+ this.setState({ dark: mutation.target.classList.contains('dark') });
+ }
+ });
+ });
+ }
+
+ componentDidMount() {
+ this.mutationObserver.observe(document.querySelector('html'), { attributes: true });
+ }
+
+ componentWillUnmount() {
+ this.mutationObserver.disconnect();
+ }
+
+ render() {
+ return (
+
+
+
+ );
+ }
+ }
+}
diff --git a/assets/static/images/icon/plausible_logo_100px_dark.png b/assets/static/images/icon/plausible_logo_100px_dark.png
new file mode 100644
index 000000000000..148b43164e36
Binary files /dev/null and b/assets/static/images/icon/plausible_logo_100px_dark.png differ
diff --git a/assets/static/images/icon/plausible_logo_300px_dark.png b/assets/static/images/icon/plausible_logo_300px_dark.png
new file mode 100644
index 000000000000..0baea1e7f6de
Binary files /dev/null and b/assets/static/images/icon/plausible_logo_300px_dark.png differ
diff --git a/assets/static/images/icon/plausible_logo_dark.png b/assets/static/images/icon/plausible_logo_dark.png
new file mode 100644
index 000000000000..f386bdfc5aae
Binary files /dev/null and b/assets/static/images/icon/plausible_logo_dark.png differ
diff --git a/assets/static/js/applyTheme.js b/assets/static/js/applyTheme.js
new file mode 100644
index 000000000000..2988d7d5908c
--- /dev/null
+++ b/assets/static/js/applyTheme.js
@@ -0,0 +1,37 @@
+var pref = document.currentScript.dataset.pref;
+
+function reapplyTheme() {
+ var userPref = pref || "system";
+ var mediaPref = window.matchMedia('(prefers-color-scheme: dark)').matches;
+ var htmlRef = document.querySelector('html');
+ var hcaptchaRefs = document.getElementsByClassName('h-captcha');
+
+ htmlRef.classList.remove('dark');
+ for (let i = 0; i < hcaptchaRefs.length; i++) {
+ hcaptchaRefs[i].dataset.theme = "light";
+ }
+
+ switch (userPref) {
+ case "dark":
+ htmlRef.classList.add('dark');
+ for (let i = 0; i < hcaptchaRefs.length; i++) {
+ hcaptchaRefs[i].dataset.theme = "dark";
+ }
+ break;
+ case "system":
+ if (mediaPref) {
+ htmlRef.classList.add('dark');
+ for (let i = 0; i < hcaptchaRefs.length; i++) {
+ hcaptchaRefs[i].dataset.theme = "dark";
+ }
+ }
+ break;
+ }
+}
+
+reapplyTheme();
+window.matchMedia('(prefers-color-scheme: dark)').addListener(reapplyTheme);
+
+window.onload = function() {
+ reapplyTheme();
+};
diff --git a/assets/tailwind.config.js b/assets/tailwind.config.js
index d59180f6b4e9..13c84b7d98a1 100644
--- a/assets/tailwind.config.js
+++ b/assets/tailwind.config.js
@@ -5,7 +5,7 @@ module.exports = {
'./js/**/*.js',
'../lib/plausible_web/templates/**/*.html.eex',
],
- darkMode: false,
+ darkMode: 'class',
theme: {
container: {
center: true,
@@ -14,18 +14,29 @@ module.exports = {
extend: {
colors: {
orange: colors.orange,
+ 'gray-850': 'rgb(26, 32, 44)',
+ 'gray-825': 'rgb(37, 47, 63)'
},
spacing: {
'44': '11rem'
},
width: {
'31percent': '31%',
+ },
+ opacity: {
+ '15': '0.15',
}
},
},
variants: {
textColor: ['responsive', 'hover', 'focus', 'group-hover'],
- display: ['responsive', 'hover', 'focus', 'group-hover']
+ display: ['responsive', 'hover', 'focus', 'group-hover'],
+ extend: {
+ textColor: ['dark'],
+ borderWidth: ['dark'],
+ backgroundOpacity: ['dark'],
+ display: ['dark'],
+ }
},
plugins: [
require('@tailwindcss/forms'),
diff --git a/lib/plausible/auth/user.ex b/lib/plausible/auth/user.ex
index 7a6874ca41c1..eef1714b72f1 100644
--- a/lib/plausible/auth/user.ex
+++ b/lib/plausible/auth/user.ex
@@ -17,6 +17,7 @@ defmodule Plausible.Auth.User do
field :name, :string
field :last_seen, :naive_datetime
field :trial_expiry_date, :date
+ field :theme, :string
field :email_verified, :boolean
has_many :site_memberships, Plausible.Site.Membership
@@ -40,7 +41,7 @@ defmodule Plausible.Auth.User do
def changeset(user, attrs \\ %{}) do
user
- |> cast(attrs, [:email, :name, :email_verified])
+ |> cast(attrs, [:email, :name, :email_verified, :theme])
|> validate_required([:email, :name, :email_verified])
|> unique_constraint(:email)
end
diff --git a/lib/plausible/themes.ex b/lib/plausible/themes.ex
new file mode 100644
index 000000000000..5772bfaa5dcc
--- /dev/null
+++ b/lib/plausible/themes.ex
@@ -0,0 +1,11 @@
+defmodule Plausible.Themes do
+ @options [
+ [key: "Follow System Theme", value: "system"],
+ [key: "Light", value: "light"],
+ [key: "Dark", value: "dark"]
+ ]
+
+ def options() do
+ @options
+ end
+end
diff --git a/lib/plausible_web/controllers/auth_controller.ex b/lib/plausible_web/controllers/auth_controller.ex
index a62dc0a76103..0361315c62f6 100644
--- a/lib/plausible_web/controllers/auth_controller.ex
+++ b/lib/plausible_web/controllers/auth_controller.ex
@@ -272,7 +272,8 @@ defmodule PlausibleWeb.AuthController do
render(conn, "user_settings.html",
changeset: changeset,
- subscription: conn.assigns[:current_user].subscription
+ subscription: conn.assigns[:current_user].subscription,
+ theme: conn.assigns[:current_user].theme || "system"
)
end
diff --git a/lib/plausible_web/templates/auth/_onboarding_steps.html.eex b/lib/plausible_web/templates/auth/_onboarding_steps.html.eex
index c685d3a71abb..b888b0fd89f7 100644
--- a/lib/plausible_web/templates/auth/_onboarding_steps.html.eex
+++ b/lib/plausible_web/templates/auth/_onboarding_steps.html.eex
@@ -6,30 +6,30 @@
-
-
- <%= step %>
+ <%= step %>
<% end %>
<%= if index == @current_step do %>
-
-
-
+
+
- <%= step %>
+ <%= step %>
<% end %>
<%= if index > @current_step do %>
-
-
<%= step %>
+ <%= step %>
<% end %>
<% end %>
diff --git a/lib/plausible_web/templates/auth/activate.html.eex b/lib/plausible_web/templates/auth/activate.html.eex
index c152fe7df94d..933bfb0e4bbb 100644
--- a/lib/plausible_web/templates/auth/activate.html.eex
+++ b/lib/plausible_web/templates/auth/activate.html.eex
@@ -1,32 +1,32 @@
<%= if @has_pin do %>
- <%= form_for @conn, "/activate", [class: "w-full max-w-lg mx-auto bg-white shadow-md rounded px-8 py-6 mb-4 mt-8"], fn f -> %>
- Activate your account
+ <%= form_for @conn, "/activate", [class: "w-full max-w-lg mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8"], fn f -> %>
+ Activate your account
-
+
Please enter the 4-digit code we sent to <%= @conn.assigns[:current_user].email %>
- <%= text_input f, :code, class: "tracking-widest font-medium shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-36 px-8 border-gray-300 rounded-l-md", oninput: "this.value=this.value.replace(/[^0-9]/g, ''); if (this.value.length >= 4) document.getElementById('submit').focus()", onclick: "this.select();", maxlength: "4", placeholder: "••••", style: "letter-spacing: 10px;" %>
+ <%= text_input f, :code, class: "tracking-widest font-medium shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-36 px-8 border-gray-300 dark:border-gray-500 rounded-l-md dark:text-gray-200 dark:bg-gray-900", oninput: "this.value=this.value.replace(/[^0-9]/g, ''); if (this.value.length >= 4) document.getElementById('submit').focus()", onclick: "this.select();", maxlength: "4", placeholder: "••••", style: "letter-spacing: 10px;" %>
<%= error_tag(assigns, :error) %>
-
+
Didn't receive an email?
- Please check your spam folder and contact support@plausible.io if the problem persists
+ Please check your spam folder and contact support@plausible.io if the problem persists
<% end %>
<% else %>
-
- Activate your account
+
+ Activate your account
-
+
A 4-digit activation code will be sent to <%= @conn.assigns[:current_user].email %>
diff --git a/lib/plausible_web/templates/auth/login_form.html.eex b/lib/plausible_web/templates/auth/login_form.html.eex
index cf85de8659c8..fbb9eb9fcd9e 100644
--- a/lib/plausible_web/templates/auth/login_form.html.eex
+++ b/lib/plausible_web/templates/auth/login_form.html.eex
@@ -1,5 +1,5 @@
-<%= form_for @conn, "/login", [class: "w-full max-w-md mx-auto bg-white shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
- <%= get_flash(@conn, :login_title) || "Enter your email and password" %>
+<%= form_for @conn, "/login", [class: "w-full max-w-md mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
+ <%= get_flash(@conn, :login_title) || "Enter your email and password" %>
<%= if get_flash(@conn, :login_instructions) do %>
<%= get_flash(@conn, :login_instructions) %>
<% end %>
@@ -7,18 +7,18 @@
<%= @conn.assigns[:error] %>
<% end %>
- <%= label f, :email, class: "block text-gray-700 text-sm font-bold mb-2" %>
- <%= email_input f, :email, class: "bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300", placeholder: "user@example.com" %>
+ <%= label f, :email, class: "block text-gray-700 dark:text-gray-300 text-sm font-bold mb-2" %>
+ <%= email_input f, :email, class: "bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500", placeholder: "user@example.com" %>
- <%= label f, :password, class: "block text-gray-700 text-sm font-bold mb-2" %>
- <%= password_input f, :password, class: "transition bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300" %>
- Forgot password? Click here to reset it.
+ <%= label f, :password, class: "block text-gray-700 dark:text-gray-300 text-sm font-bold mb-2" %>
+ <%= password_input f, :password, class: "transition bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500" %>
+ Forgot password? Click here to reset it.
<%= submit "Login →", class: "button mt-4 w-full" %>
<%= if !Keyword.fetch!(Application.get_env(:plausible, :selfhost),:disable_registration) do %>
- Don't have an account? <%= link("Register", to: "/register", class: "underline text-gray-800") %> instead.
+ Don't have an account? <%= link("Register", to: "/register", class: "text-gray-800 dark:text-gray-50 underline") %> instead.
<% end %>
<% end %>
diff --git a/lib/plausible_web/templates/auth/password_form.html.eex b/lib/plausible_web/templates/auth/password_form.html.eex
index f4c4f651e30b..70a5d9a00c61 100644
--- a/lib/plausible_web/templates/auth/password_form.html.eex
+++ b/lib/plausible_web/templates/auth/password_form.html.eex
@@ -1,14 +1,14 @@
-<%= form_for @conn, "/password", [class: "bg-white max-w-md w-full mx-auto shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
- Set your password
+<%= form_for @conn, "/password", [class: "bg-white dark:bg-gray-800 max-w-md w-full mx-auto shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
+ Set your password
- Min 6 characters
- <%= password_input f, :password, class: "transition bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300" %>
+ Min 6 characters
+ <%= password_input f, :password, class: "transition bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500" %>
<%= if @conn.assigns[:changeset] do %>
<%= error_tag @changeset, :password %>
<% end %>
<%= submit "Set password →", class: "button mt-4 w-full" %>
-
- Don't have an account? <%= link("Register", to: "/register") %> instead.
+
+ Don't have an account? <%= link("Register", to: "/register", class: "underline text-gray-800 dark:text-gray-200") %> instead.
<% end %>
diff --git a/lib/plausible_web/templates/auth/password_reset_form.html.eex b/lib/plausible_web/templates/auth/password_reset_form.html.eex
index 84c172acfc1e..c405aefbfd8e 100644
--- a/lib/plausible_web/templates/auth/password_reset_form.html.eex
+++ b/lib/plausible_web/templates/auth/password_reset_form.html.eex
@@ -1,15 +1,15 @@
-<%= form_for @conn, "/password/reset", [class: "bg-white max-w-md w-full mx-auto shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
- Reset your password
+<%= form_for @conn, "/password/reset", [class: "bg-white dark:bg-gray-800 max-w-md w-full mx-auto shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
+ Reset your password
- Min 6 characters
- <%= password_input f, :password, class: "transition bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300" %>
+ Min 6 characters
+ <%= password_input f, :password, class: "transition bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500" %>
<%= if @conn.assigns[:changeset] do %>
<%= error_tag @changeset, :password %>
<% end %>
<%= hidden_input f, :token, value: @token %>
<%= submit "Set password →", class: "button mt-4 w-full" %>
-
- Don't have an account? <%= link("Register", to: "/register") %> instead.
+
+ Don't have an account? <%= link("Register", to: "/register", class: "underline text-gray-800 dark:text-gray-200") %> instead.
<% end %>
diff --git a/lib/plausible_web/templates/auth/password_reset_request_form.html.eex b/lib/plausible_web/templates/auth/password_reset_request_form.html.eex
index b5e87e5d69c5..53cadd4a86de 100644
--- a/lib/plausible_web/templates/auth/password_reset_request_form.html.eex
+++ b/lib/plausible_web/templates/auth/password_reset_request_form.html.eex
@@ -1,8 +1,8 @@
-<%= form_for @conn, "/password/request-reset", [class: "max-w-md w-full mx-auto bg-white shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
- Reset your password
- Enter your email so we can send a password reset link
+<%= form_for @conn, "/password/request-reset", [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
+ Reset your password
+ Enter your email so we can send a password reset link
- <%= email_input f, :email, class: "transition bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300", placeholder: "user@example.com" %>
+ <%= email_input f, :email, class: "transition bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500", placeholder: "user@example.com" %>
<%= if @conn.assigns[:error] do %>
<%= @conn.assigns[:error] %>
diff --git a/lib/plausible_web/templates/auth/password_reset_request_success.html.eex b/lib/plausible_web/templates/auth/password_reset_request_success.html.eex
index 03652b87abaf..6769c7b09068 100644
--- a/lib/plausible_web/templates/auth/password_reset_request_success.html.eex
+++ b/lib/plausible_web/templates/auth/password_reset_request_success.html.eex
@@ -1,12 +1,12 @@
-
- Success!
-
+
+ Success!
+
We have sent an email with password reset instructions to <%= @email %> if it exists in our database.
-
+
Didn't receive an email?
-
- Please check your spam folder and contact support@plausible.io if the problem persists
+
+ Please check your spam folder and contact support@plausible.io if the problem persists
diff --git a/lib/plausible_web/templates/auth/register_form.html.eex b/lib/plausible_web/templates/auth/register_form.html.eex
index 14a4dce4f7d9..5d43b1494745 100644
--- a/lib/plausible_web/templates/auth/register_form.html.eex
+++ b/lib/plausible_web/templates/auth/register_form.html.eex
@@ -1,51 +1,51 @@
-
+
Register your 30-day unlimited-use free trial
Set up privacy-friendly analytics with just a few clicks
- <%= form_for @changeset, "/register", [class: "w-full max-w-md mx-auto bg-white shadow-md rounded px-8 py-6 mb-4 mt-8", id: "register-form"], fn f -> %>
- Enter your details
+ <%= form_for @changeset, "/register", [class: "w-full max-w-md mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8", id: "register-form"], fn f -> %>
+ Enter your details
- <%= label f, :name, "Full name", class: "block text-sm font-medium text-gray-700" %>
+ <%= label f, :name, "Full name", class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
- <%= text_input f, :name, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md", placeholder: "Jane Doe" %>
+ <%= text_input f, :name, class: "dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300", placeholder: "Jane Doe" %>
<%= error_tag f, :name %>
- <%= label f, :email, class: "block text-sm font-medium text-gray-700" %>
+ <%= label f, :email, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
No spam, guaranteed.
- <%= email_input f, :email, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md", placeholder: "example@email.com" %>
+ <%= email_input f, :email, class: "dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300", placeholder: "example@email.com" %>
<%= error_tag f, :email %>
- <%= label f, :password, class: "block text-sm font-medium text-gray-700" %>
+ <%= label f, :password, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
Min 6 characters
- <%= password_input f, :password, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md" %>
+ <%= password_input f, :password, class: "dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300" %>
<%= error_tag f, :password %>
- <%= label f, :password_confirmation, class: "block text-sm font-medium text-gray-700" %>
+ <%= label f, :password_confirmation, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
- <%= password_input f, :password_confirmation, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md" %>
+ <%= password_input f, :password_confirmation, class: "dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300" %>
<%= error_tag f, :password_confirmation %>
<%= if PlausibleWeb.Captcha.enabled?() do %>
-
+
<%= if assigns[:captcha_error] do %>
<%= @captcha_error %>
<% end %>
@@ -55,8 +55,8 @@
<%= submit "Start my free trial →", class: "button mt-4 w-full" %>
-
- Already have an account? <%= link("Log in", to: "/login", class: "underline text-gray-800") %> instead.
+
+ Already have an account? <%= link("Log in", to: "/login", class: "underline text-gray-800 dark:text-gray-50") %> instead.
<% end %>
diff --git a/lib/plausible_web/templates/auth/register_success.html.eex b/lib/plausible_web/templates/auth/register_success.html.eex
index 24dec1aa88d4..6fb2c51be111 100644
--- a/lib/plausible_web/templates/auth/register_success.html.eex
+++ b/lib/plausible_web/templates/auth/register_success.html.eex
@@ -1,20 +1,20 @@
-
+
Register your 30-day unlimited-use free trial
Set up privacy-friendly analytics with just a few clicks
- <%= form_for @conn, "/claim-activation", [class: "w-full max-w-lg mx-auto bg-white shadow-md rounded px-8 py-6 mb-4 mt-8", id: "register-form"], fn f -> %>
- Activate your account
+ <%= form_for @conn, "/claim-activation", [class: "w-full max-w-lg mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8", id: "register-form"], fn f -> %>
+ Activate your account
-
+
Please enter the 4-digit code we sent to <%= @email %>
- <%= text_input f, :code, class: "tracking-widest font-medium shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-36 px-8 border-gray-300 rounded-l-md", oninput: "this.value=this.value.replace(/[^0-9]/g, ''); if (this.value.length >= 4) document.getElementById('submit').focus()", onclick: "this.select();", maxlength: "4", placeholder: "••••", style: "letter-spacing: 10px;" %>
+ <%= text_input f, :code, class: "tracking-widest font-medium shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-36 px-8 border-gray-300 dark:border-gray-500 rounded-l-md dark:text-gray-200 dark:bg-gray-900", oninput: "this.value=this.value.replace(/[^0-9]/g, ''); if (this.value.length >= 4) document.getElementById('submit').focus()", onclick: "this.select();", maxlength: "4", placeholder: "••••", style: "letter-spacing: 10px;" %>
<%= error_tag f, :name %>
@@ -24,7 +24,7 @@
Didn't receive an email?
- Please check your spam folder and contact support@plausible.io if the problem persists
+ Please check your spam folder and contact support@plausible.io if the problem persists
<% end %>
diff --git a/lib/plausible_web/templates/auth/user_settings.html.eex b/lib/plausible_web/templates/auth/user_settings.html.eex
index 858eb5135fc8..08ae26a08b48 100644
--- a/lib/plausible_web/templates/auth/user_settings.html.eex
+++ b/lib/plausible_web/templates/auth/user_settings.html.eex
@@ -1,7 +1,7 @@
<%= if !Keyword.fetch!(Application.get_env(:plausible, :selfhost), :disable_subscription) do %>
-
+
- Subscription Plan
+ Subscription Plan
<%= if @subscription do %>
<%= present_subscription_status(@subscription.status) %>
@@ -31,48 +31,48 @@
<% end %>
-
- Monthly quota
+
+ Monthly quota
<%= if @subscription do %>
- <%= subscription_quota(@subscription) %> pageviews
+ <%= subscription_quota(@subscription) %> pageviews
<%= case @subscription.status do %>
<% "active" -> %>
<%= link("Change plan", to: "/billing/change-plan", class: "text-sm text-indigo-500 font-medium") %>
<% "past_due" -> %>
- Change plan
+ Change plan
<% _ -> %>
<% end %>
<% else %>
- Free trial
+ Free trial
<%= link("Upgrade", to: "/billing/upgrade", class: "text-sm text-indigo-500 font-medium") %>
<% end %>
-
- Next bill amount
+
+ Next bill amount
<%= if @subscription && @subscription.status in ["active", "past_due"] do %>
- $<%= @subscription.next_bill_amount %>
+ $<%= @subscription.next_bill_amount %>
<%= if @subscription.update_url do %>
<%= link("Update billing info", to: @subscription.update_url, class: "text-sm text-indigo-500 font-medium") %>
<% end %>
<% else %>
- ---
+ ---
<% end %>
-
- Next bill date
+
+ Next bill date
<%= if @subscription && @subscription.next_bill_date && @subscription.status in ["active", "past_due"] do %>
- <%= Timex.format!(@subscription.next_bill_date, "{Mshort} {D}, {YYYY}") %>
- (<%= subscription_interval(@subscription) %> billing)
+ <%= Timex.format!(@subscription.next_bill_date, "{Mshort} {D}, {YYYY}") %>
+ (<%= subscription_interval(@subscription) %> billing)
<% else %>
- ---
+ ---
<% end %>
- Your usage
+ Your usage
-
+
<%= delimit_integer(Plausible.Billing.usage(@conn.assigns[:current_user])) %>
pageviews in the last 30 days
@@ -80,7 +80,7 @@
<%= cond do %>
<% @subscription && @subscription.status in ["active", "past_due", "paused"] && @subscription.cancel_url -> %>
- <%= link("Cancel my subscription", to: @subscription.cancel_url, class: "inline-block mt-4 px-4 py-2 border border-gray-300 text-sm leading-5 font-medium rounded-md text-red-700 bg-white hover:text-red-500 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150") %>
+ <%= link("Cancel my subscription", to: @subscription.cancel_url, class: "inline-block mt-4 px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150") %>
<% true -> %>
@@ -90,23 +90,37 @@
<% end %>
-
- Account settings
+
+ Dashboard Appearance
-
+
+
+ <%= form_for @changeset, "/settings", [class: "max-w-sm"], fn f -> %>
+
+ <%= label f, :theme, "Theme Selection", class: "block text-sm font-medium leading-5 text-gray-700 dark:text-gray-300" %>
+ <%= select f, :theme, Plausible.Themes.options(), class: "dark:bg-gray-900 mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md dark:text-gray-100 cursor-pointer" %>
+
+ <%= submit "Save", class: "button mt-4" %>
+ <% end %>
+
+
+
+ Account settings
+
+
<%= form_for @changeset, "/settings", [class: "max-w-sm"], fn f -> %>
- <%= label f, :name, class: "block text-sm font-medium text-gray-700" %>
+ <%= label f, :name, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
- <%= text_input f, :name, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md" %>
+ <%= text_input f, :name, class: "shadow-sm dark:bg-gray-900 dark:text-gray-300 focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:bg-gray-800" %>
<%= error_tag f, :name %>
- <%= label f, :email, class: "block text-sm font-medium text-gray-700" %>
+ <%= label f, :email, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
- <%= email_input f, :email, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md" %>
+ <%= email_input f, :email, class: "shadow-sm dark:bg-gray-900 dark:text-gray-300 focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md" %>
<%= error_tag f, :email %>
@@ -114,19 +128,19 @@
<% end %>
-
+
- Delete account
+ Delete account
-
+
- Deleting your account removes all sites and stats you've collected
+ Deleting your account removes all sites and stats you've collected
<%= if @subscription && @subscription.status == "active" do %>
- Delete my account
- Your account cannot be deleted because you have an active subscription. If you want to delete your account, please cancel your subscription first.
+ Delete my account
+ Your account cannot be deleted because you have an active subscription. If you want to delete your account, please cancel your subscription first.
<% else %>
- <%= link("Delete my account", to: "/me", class: "inline-block mt-4 px-4 py-2 border border-gray-300 text-sm leading-5 font-medium rounded-md text-red-700 bg-white hover:text-red-500 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150", method: "delete", data: [confirm: "Deleting your account cannot be reversed. Are you sure?"]) %>
+ <%= link("Delete my account", to: "/me", class: "inline-block mt-4 px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150", method: "delete", data: [confirm: "Deleting your account cannot be reversed. Are you sure?"]) %>
<% end %>
diff --git a/lib/plausible_web/templates/billing/change_plan.html.eex b/lib/plausible_web/templates/billing/change_plan.html.eex
index ca72f44f63fc..082a0c1bc1f7 100644
--- a/lib/plausible_web/templates/billing/change_plan.html.eex
+++ b/lib/plausible_web/templates/billing/change_plan.html.eex
@@ -3,16 +3,16 @@
- Change subscription plan
+ Change subscription plan
-
-
+
+
Select your new plan
-
+
Depending on which plan you choose, your card might be charged immediately and your
next payment date could change. You can preview these changes before committing.
@@ -20,12 +20,12 @@
- | |