Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

T345056: dark mode #186

Merged
merged 13 commits into from
Mar 6, 2024
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ popupContainer | DOM Element | `document.body` | Where to put the popup in the D
detectLinks | Boolean | `false` | Allow Wikipedia hyperlinks to have the popup
events | Object | `{}` | Custom event handlers: `{ onShow: <fn>, onWikiRead: <fn> }`
debug | Boolean | `false` | Shows the debug message when `init()` is called
prefersColorScheme | string | `'detect'` | Sets theme color. Allowed values are 'light', 'dark' and 'detect'. Setting it to 'light' or 'dark' will dictate theme color regardless of [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme); setting to 'detect' will render preview according to [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme).
hueitan marked this conversation as resolved.
Show resolved Hide resolved

Example (custom selector)
```html
Expand Down Expand Up @@ -159,6 +160,20 @@ If you prefer to style them in a way that makes more sense for your context, sim
}
```

#### CSS custom properties
If you wish to adjust the styling of the light/dark theme, you can override the following CSS custom properties to your liking as shown below, under the appropriate [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) query.
hueitan marked this conversation as resolved.
Show resolved Hide resolved

```CSS
@media (prefers-color-scheme: dark) {
.wikipediapreview {
--wikipediapreview-primary-background-color: #202122;
--wikipediapreview-secondary-background-color: #202122;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent

--wikipediapreview-primary-color: #eaecf0;
--wikipediapreview-filter-setting: invert(1);
}
}
```

## Acknowledgements/Contributors

This is heavily inspired by [jquery.wikilookup](https://github.com/mooeypoo/jquery.wikilookup) and [Page Previews](https://www.mediawiki.org/wiki/Page_Previews).
15 changes: 14 additions & 1 deletion demo/articles/spanish.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@
<link href="../css/article.css" rel="stylesheet" type="text/css"/>
<link href="../css/link.css" rel="stylesheet" type="text/css"/>
<style>
body {
background-color: #202122;

p {
color: #EAECF0;
}
}

.header {
color: #36c;
}

@media screen and (max-width: 650px) {
.customize-background-position {
background-position-y: -30px !important;
Expand Down Expand Up @@ -67,7 +79,8 @@
<script src="/dist/wikipedia-preview.umd.cjs"></script>
<script>
wikipediaPreview.init({
lang: 'es'
lang: 'es',
prefersColorScheme: 'dark'
});
</script>
<!-- End of Scripts -->
Expand Down
25 changes: 18 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ function init( {
detectLinks = false,
popupContainer = document.body,
events = {},
debug = false
debug = false,
prefersColorScheme = 'detect'
} ) {
const globalLang = lang
const popup = isTouch ?
Expand Down Expand Up @@ -99,7 +100,11 @@ function init( {

popup.loading = true
popup.dir = dir
popup.show( renderLoading( isTouch, localLang, dir ), currentTarget, pointerPosition )
popup.show(
renderLoading( isTouch, localLang, dir, prefersColorScheme ),
currentTarget,
pointerPosition
)

requestPagePreview( localLang, title, ( data ) => {
if ( popupId !== currentPopupId ) {
Expand All @@ -112,16 +117,22 @@ function init( {
popup.title = title
if ( data.type === 'standard' ) {
popup.show(
renderPreview( localLang, data, isTouch ),
renderPreview( localLang, data, isTouch, prefersColorScheme ),
currentTarget,
pointerPosition
)
invokeCallback( events, 'onShow', [ title, localLang, 'standard' ] )
} else if ( data.type === 'disambiguation' ) {
const content = data.extractHtml ?
renderPreview( localLang, data, isTouch ) :
renderPreview( localLang, data, isTouch, prefersColorScheme ) :
// fallback message when no extract is found on disambiguation page
renderDisambiguation( isTouch, localLang, data.title, data.dir )
renderDisambiguation(
isTouch,
localLang,
data.title,
data.dir,
prefersColorScheme
)
popup.show(
content,
currentTarget,
Expand All @@ -132,14 +143,14 @@ function init( {
} else {
if ( isOnline() ) {
popup.show(
renderError( isTouch, localLang, title, dir ),
renderError( isTouch, localLang, title, dir, prefersColorScheme ),
currentTarget,
pointerPosition
)
invokeCallback( events, 'onShow', [ title, localLang, 'error' ] )
} else {
popup.show(
renderOffline( isTouch, localLang, dir ),
renderOffline( isTouch, localLang, dir, prefersColorScheme ),
currentTarget,
pointerPosition
)
Expand Down
40 changes: 24 additions & 16 deletions src/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,21 @@ const getPreviewBody = ( type, message, cta ) => {
`.trim()
}

const render = ( lang, isTouch, dir, headerContent, bodyContent ) => {
const getReadOnWikiCta = ( lang, title, isTouch ) => {
return `<a href="${ buildWikipediaUrl( lang, title, isTouch ) }" target="_blank" class="wikipediapreview-cta-readonwiki">${ msg( lang, 'read-on-wiki' ) }</a>`
}

const render = ( lang, isTouch, dir, headerContent, bodyContent, prefersColorScheme ) => {
const colorScheme = prefersColorScheme === 'detect' ? '' : `wikipediapreview-${ prefersColorScheme }-theme`
return `
<div class="wikipediapreview ${ isTouch ? 'mobile' : '' }" lang="${ lang }" dir="${ dir }">
<div class="wikipediapreview ${ isTouch ? 'mobile' : '' } ${ colorScheme }" lang="${ lang }" dir="${ dir }">
${ headerContent }
${ bodyContent }
</div>
`.trim()
}

const renderPreview = ( lang, data, isTouch ) => {
const renderPreview = ( lang, data, isTouch, prefersColorScheme ) => {
const imageUrl = data.imgUrl,
bodyContent = `
<div class="wikipediapreview-body">
Expand All @@ -49,10 +54,17 @@ const renderPreview = ( lang, data, isTouch ) => {
</div>
`.trim()

return render( lang, isTouch, data.dir, getPreviewHeader( lang, imageUrl ), bodyContent )
return render(
lang,
isTouch,
data.dir,
getPreviewHeader( lang, imageUrl ),
bodyContent,
prefersColorScheme
)
}

const renderLoading = ( isTouch, lang, dir ) => {
const renderLoading = ( isTouch, lang, dir, prefersColorScheme ) => {
const bodyContent = `
<div class="wikipediapreview-body wikipediapreview-body-loading">
<div class="wikipediapreview-body-loading-line larger"></div>
Expand All @@ -69,32 +81,28 @@ const renderLoading = ( isTouch, lang, dir ) => {
<div class="wikipediapreview-footer-loading"></div>
`.trim()

return render( lang, isTouch, dir, getPreviewHeader( lang ), bodyContent )
}

const getReadOnWikiCta = ( lang, title, isTouch ) => {
return `<a href="${ buildWikipediaUrl( lang, title, isTouch ) }" target="_blank" class="wikipediapreview-cta-readonwiki">${ msg( lang, 'read-on-wiki' ) }</a>`
return render( lang, isTouch, dir, getPreviewHeader( lang ), bodyContent, prefersColorScheme )
}

const renderError = ( isTouch, lang, title, dir ) => {
const renderError = ( isTouch, lang, title, dir, prefersColorScheme ) => {
const message = `<span>${ msg( lang, 'preview-error-message' ) }</span>`
const cta = getReadOnWikiCta( lang, title, isTouch )

return render( lang, isTouch, dir, getPreviewHeader( lang ), getPreviewBody( 'error', message, cta ) )
return render( lang, isTouch, dir, getPreviewHeader( lang ), getPreviewBody( 'error', message, cta ), prefersColorScheme )
}

const renderDisambiguation = ( isTouch, lang, title, dir ) => {
const renderDisambiguation = ( isTouch, lang, title, dir, prefersColorScheme ) => {
const message = `<span>${ msg( lang, 'preview-disambiguation-message', title ) }</span>`
const cta = getReadOnWikiCta( lang, title, isTouch )

return render( lang, isTouch, dir, getPreviewHeader( lang ), getPreviewBody( 'disambiguation', message, cta ) )
return render( lang, isTouch, dir, getPreviewHeader( lang ), getPreviewBody( 'disambiguation', message, cta ), prefersColorScheme )
}

const renderOffline = ( isTouch, lang, dir ) => {
const renderOffline = ( isTouch, lang, dir, prefersColorScheme ) => {
const message = `<span>${ msg( lang, 'preview-offline-message' ) }</span>`
const cta = `<a>${ msg( lang, 'preview-offline-cta' ) }</a>`

return render( lang, isTouch, dir, getPreviewHeader( lang ), getPreviewBody( 'offline', message, cta ) )
return render( lang, isTouch, dir, getPreviewHeader( lang ), getPreviewBody( 'offline', message, cta ), prefersColorScheme )
}

export { renderPreview, renderLoading, renderError, renderDisambiguation, renderOffline }
40 changes: 23 additions & 17 deletions src/stories/preview.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
imgUrl: {
name: 'Thumbnail URL',
control: 'text'
},
prefersColorScheme: {
name: 'Color Scheme',
control: 'inline-radio',
options: [ 'light', 'dark', 'detect' ]
}
},
args: {
Expand All @@ -42,21 +47,22 @@
title: 'Cat',
pageUrl: 'https://en.wikipedia.org/wiki/Cat',
extractHtml: '<p><strong>Lorem ipsum dolor sit amet,</strong> consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. <br/><br/>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>',
imgUrl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Moons_of_solar_system-he.svg/langhe-320px-Moons_of_solar_system-he.svg.png'
imgUrl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Moons_of_solar_system-he.svg/langhe-320px-Moons_of_solar_system-he.svg.png',
prefersColorScheme: 'light'
}
}

export const StandardWithImage = ( { lang, title, extractHtml, dir, pageUrl, imgUrl, touch } ) => {
return renderPreview( lang, { title, extractHtml, dir, pageUrl, imgUrl }, touch )
export const StandardWithImage = ( { lang, title, extractHtml, dir, pageUrl, imgUrl, touch, prefersColorScheme } ) => {

Check warning on line 55 in src/stories/preview.stories.js

View workflow job for this annotation

GitHub Actions / build (17)

This line has a length of 119. Maximum allowed is 100

Check warning on line 55 in src/stories/preview.stories.js

View workflow job for this annotation

GitHub Actions / build (latest)

This line has a length of 119. Maximum allowed is 100
return renderPreview( lang, { title, extractHtml, dir, pageUrl, imgUrl }, touch, prefersColorScheme )

Check warning on line 56 in src/stories/preview.stories.js

View workflow job for this annotation

GitHub Actions / build (17)

This line has a length of 105. Maximum allowed is 100

Check warning on line 56 in src/stories/preview.stories.js

View workflow job for this annotation

GitHub Actions / build (latest)

This line has a length of 105. Maximum allowed is 100
}

export const Standard = ( { lang, title, extractHtml, dir, pageUrl, touch } ) => {
return renderPreview( lang, { title, extractHtml, dir, pageUrl }, touch )
export const Standard = ( { lang, title, extractHtml, dir, pageUrl, touch, prefersColorScheme } ) => {

Check warning on line 59 in src/stories/preview.stories.js

View workflow job for this annotation

GitHub Actions / build (17)

This line has a length of 102. Maximum allowed is 100

Check warning on line 59 in src/stories/preview.stories.js

View workflow job for this annotation

GitHub Actions / build (latest)

This line has a length of 102. Maximum allowed is 100
return renderPreview( lang, { title, extractHtml, dir, pageUrl }, touch, prefersColorScheme )
}

export const Expanded = ( { lang, title, extractHtml, dir, pageUrl, touch } ) => {
export const Expanded = ( { lang, title, extractHtml, dir, pageUrl, touch, prefersColorScheme } ) => {

Check warning on line 63 in src/stories/preview.stories.js

View workflow job for this annotation

GitHub Actions / build (17)

This line has a length of 102. Maximum allowed is 100

Check warning on line 63 in src/stories/preview.stories.js

View workflow job for this annotation

GitHub Actions / build (latest)

This line has a length of 102. Maximum allowed is 100
const template = document.createElement( 'template' )
template.innerHTML = renderPreview( lang, { title, extractHtml, dir, pageUrl }, touch )
template.innerHTML = renderPreview( lang, { title, extractHtml, dir, pageUrl }, touch, prefersColorScheme )

Check warning on line 65 in src/stories/preview.stories.js

View workflow job for this annotation

GitHub Actions / build (17)

This line has a length of 111. Maximum allowed is 100

Check warning on line 65 in src/stories/preview.stories.js

View workflow job for this annotation

GitHub Actions / build (latest)

This line has a length of 111. Maximum allowed is 100
const preview = template.content.firstChild
preview.classList.add( 'expanded' )
const mediaData = [
Expand All @@ -76,22 +82,22 @@
return preview
}

export const Loading = ( { touch, lang, dir } ) => {
return renderLoading( touch, lang, dir )
export const Loading = ( { touch, lang, dir, prefersColorScheme } ) => {
return renderLoading( touch, lang, dir, prefersColorScheme )
}

export const Error = ( { touch, lang, title, dir } ) => {
return renderError( touch, lang, title, dir )
export const Error = ( { touch, lang, title, dir, prefersColorScheme } ) => {
return renderError( touch, lang, title, dir, prefersColorScheme )
}

export const Disambiguation = ( { lang, title, extractHtml, dir, pageUrl, touch } ) => {
return renderPreview( lang, { title, extractHtml, dir, pageUrl }, touch )
export const Disambiguation = ( { lang, title, extractHtml, dir, pageUrl, touch, prefersColorScheme } ) => {

Check warning on line 93 in src/stories/preview.stories.js

View workflow job for this annotation

GitHub Actions / build (17)

This line has a length of 108. Maximum allowed is 100

Check warning on line 93 in src/stories/preview.stories.js

View workflow job for this annotation

GitHub Actions / build (latest)

This line has a length of 108. Maximum allowed is 100
return renderPreview( lang, { title, extractHtml, dir, pageUrl }, touch, prefersColorScheme )
}

export const DisambiguationWithNoExtract = ( { touch, lang, title, dir } ) => {
return renderDisambiguation( touch, lang, title, dir )
export const DisambiguationWithNoExtract = ( { touch, lang, title, dir, prefersColorScheme } ) => {
return renderDisambiguation( touch, lang, title, dir, prefersColorScheme )
}

export const Offline = ( { touch, lang, dir } ) => {
return renderOffline( touch, lang, dir )
export const Offline = ( { touch, lang, dir, prefersColorScheme } ) => {
return renderOffline( touch, lang, dir, prefersColorScheme )
}
61 changes: 42 additions & 19 deletions style/preview.less
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
display: flex;
flex-direction: column;
width: 350px;
background-color: var( --wikipediapreview-primary-background-color );
box-shadow: 0 30px 90px -20px rgba( 0, 0, 0, 0.3 ), 0 0 1px 1px rgba( 0, 0, 0, 0.05 );
border-radius: 8px 8px 0 0;
background-color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Lato', 'Helvetica', 'Arial', sans-serif;

* {
Expand Down Expand Up @@ -43,6 +43,7 @@
flex-grow: 1;
margin-left: 16px;
margin-right: 16px;
filter: var( --wikipediapreview-filter-setting );

&-with-image {
margin-top: 16px;
Expand Down Expand Up @@ -75,34 +76,20 @@
height: 100%;
text-align: center;
width: 50px;
filter: var( --wikipediapreview-filter-setting );
}
}

&-body {
background-color: #fff;
max-height: 248px;
overflow: hidden;

&:after {
content: ' ';
position: absolute;
width: 100%;
bottom: 35px;
left: 0;
right: 200px;
top: 235px;
background: -moz-linear-gradient( top, rgba( 255, 255, 255, 0 ) 0%, #fff 100% );
background: -webkit-linear-gradient( top, rgba( 255, 255, 255, 0 ) 0%, #fff 100% );
background: linear-gradient( to bottom, rgba( 255, 255, 255, 0 ) 0%, #fff 100% );
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#ffffff', GradientType=0 );
}

medied marked this conversation as resolved.
Show resolved Hide resolved
p {
margin: 0;
color: #202122;
line-height: 1.6;
font-size: 18px;
padding: 10px 20px;
color: var( --wikipediapreview-primary-color );
}

ul {
Expand All @@ -117,7 +104,7 @@
margin-right: 23px;
font-size: 16px;
line-height: 1.4;
color: #000;
color: var( --wikipediapreview-primary-color );
}

&-icon {
Expand Down Expand Up @@ -272,7 +259,7 @@

&-loading {
height: 30px;
background-color: #eaecf0;
background-color: var( --wikipediapreview-secondary-background-color );
}
}

Expand Down Expand Up @@ -352,3 +339,39 @@
flex-direction: column;
justify-content: center;
}

@media (prefers-color-scheme: dark) {
medied marked this conversation as resolved.
Show resolved Hide resolved
.wikipediapreview {
.mixin-wikipediapreview-dark-theme();
}
}

@media (prefers-color-scheme: light) {
.wikipediapreview {
.mixin-wikipediapreview-light-theme();
}
}

.wikipediapreview.wikipediapreview-dark-theme {
.mixin-wikipediapreview-dark-theme();

}

.wikipediapreview.wikipediapreview-light-theme {
.mixin-wikipediapreview-light-theme();
}


.mixin-wikipediapreview-dark-theme () {
--wikipediapreview-primary-background-color: #202122;
--wikipediapreview-secondary-background-color: #202122;
--wikipediapreview-primary-color: #eaecf0;
--wikipediapreview-filter-setting: invert(1);
}

.mixin-wikipediapreview-light-theme () {
--wikipediapreview-primary-background-color: #fff;
--wikipediapreview-secondary-background-color: #eaecf0;
--wikipediapreview-primary-color: #202122;
--wikipediapreview-filter-setting: unset;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the duplication with less mixins could be avoided with CSS custom properties (are they okay to use in this project?). Example:

/* Define default light theme custom properties */
:root {
  --background-color: #ffffff;
  --text-color: #000000;
}

/* Override custom properties for dark theme based on media query preference */
@media (prefers-color-scheme: dark) {
  :root {
    --background-color: #000000;
    --text-color: #ffffff;
  }
}

/* Explicit class for light theme */
.light-theme {
  --background-color: #ffffff;
  --text-color: #000000;
}

/* Explicit class for dark theme */
.dark-theme {
  --background-color: #000000;
  --text-color: #ffffff;
}

/* Use custom properties */
body {
  background-color: var(--background-color);
  color: var(--text-color);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why it wouldn't be okay to use them, I refactored to use CSS custom properties and indeed it seems a bit cleaner (this way seems to be more of a standard - https://web.dev/articles/prefers-color-scheme#stylesheet_architecture), thanks

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:root represent the whole document, probably the <html> or <body> tag. It's important to remember that this is a small component hosted within any website. If we want to use an approach like that, everything has to be prefixed so that even if it bleeds into the host, it has no side effects on it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LESS variables would probably give you a similar coding style but with actual isolation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's a good point, I can update using LESS variables and maybe prefix it anyway as something like --wikipediapreview-primary-background-color

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving it with prefixes for now, let's make a decision next week

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks good now i think, but can we still use mixin() here so we don't repeat the --var settings?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to repeat the styles for the "light" variants? Aren't those the defaults?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we also write in README to explain how the site owner custom their dark mode styling?

custom code like this, and explain what's the var there

@media (prefers-color-scheme: dark) {
	.wikipediapreview {
		--wikipediapreview-primary-background-color: #202122;
	        --wikipediapreview-secondary-background-color: #202122;
		--wikipediapreview-primary-color: #eaecf0;
		--wikipediapreview-filter-setting: invert(1);
	}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great feedback all, thanks