Theming & styling
Material UI themes, host page CSS and Shadow DOM.
Custom theme
Every palette option is in the theming guide.
View source — 42 lines
import {
JBrowseLinearGenomeView,
useCreateViewState,
} from '@jbrowse/react-linear-genome-view2'
export default function WithCustomTheme() {
const state = useCreateViewState({
assembly: {
name: 'volvox',
uri: 'https://jbrowse.org/genomes/volvox/volvox.2bit',
},
tracks: [
{
type: 'FeatureTrack',
trackId: 'volvox_gff3',
name: 'Volvox genes',
assemblyNames: ['volvox'],
adapter: {
type: 'Gff3TabixAdapter',
uri: 'https://jbrowse.org/code/jb2/main/test_data/volvox/volvox.sort.gff3.gz',
},
},
],
configuration: {
theme: {
palette: {
primary: { main: '#311b92' },
secondary: { main: '#0097a7' },
tertiary: { main: '#f57c00' },
quaternary: { main: '#d50000' },
bases: {
A: { main: '#98FB98' },
C: { main: '#87CEEB' },
G: { main: '#DAA520' },
T: { main: '#DC143C' },
},
},
},
},
})
return state ? <JBrowseLinearGenomeView viewState={state} /> : null
}Dark theme
View source — 35 lines
import {
JBrowseLinearGenomeView,
useCreateViewState,
} from '@jbrowse/react-linear-genome-view2'
export default function WithDarkTheme() {
const state = useCreateViewState({
assembly: {
name: 'volvox',
uri: 'https://jbrowse.org/genomes/volvox/volvox.2bit',
},
tracks: [
{
type: 'FeatureTrack',
trackId: 'volvox_gff3',
name: 'Volvox genes',
assemblyNames: ['volvox'],
adapter: {
type: 'Gff3TabixAdapter',
uri: 'https://jbrowse.org/code/jb2/main/test_data/volvox/volvox.sort.gff3.gz',
},
},
],
configuration: {
theme: {
palette: {
mode: 'dark',
primary: { main: '#333' },
secondary: { main: '#444' },
},
},
},
})
return state ? <JBrowseLinearGenomeView viewState={state} /> : null
}Styling from outside the component
View source — 31 lines
import {
JBrowseLinearGenomeView,
useCreateViewState,
} from '@jbrowse/react-linear-genome-view2'
export default function WithOutsideStyling() {
const state = useCreateViewState({
assembly: {
name: 'volvox',
uri: 'https://jbrowse.org/genomes/volvox/volvox.2bit',
},
tracks: [
{
type: 'FeatureTrack',
trackId: 'volvox_gff3',
name: 'Volvox genes',
assemblyNames: ['volvox'],
adapter: {
type: 'Gff3TabixAdapter',
uri: 'https://jbrowse.org/code/jb2/main/test_data/volvox/volvox.sort.gff3.gz',
},
},
],
location: 'ctgA:1105..1221',
})
return (
<div style={{ textAlign: 'center', fontFamily: 'monospace' }}>
{state ? <JBrowseLinearGenomeView viewState={state} /> : null}
</div>
)
}Package as a custom element
Menus, tooltips and dialogs portal to document.body by default, outside the
shadow root, where the emotion cache cannot style them. The source points the
cache and each portal container inside it.
View source — 103 lines
import { createElement, useEffect, useRef, useState } from 'react'
import createCache from '@emotion/cache'
import { CacheProvider } from '@emotion/react'
import {
JBrowseLinearGenomeView,
createViewState,
destroyViewState,
} from '@jbrowse/react-linear-genome-view2'
import r2wc from '@r2wc/react-to-web-component'
import { createPortal } from 'react-dom'
import type { EmotionCache } from '@emotion/cache'
import type { ViewModel } from '@jbrowse/react-linear-genome-view2'
const ShadowComponent = () => {
const nodeRef = useRef<HTMLDivElement>(null)
const nodeForPinRef = useRef(null)
const [rootNode, setRootNode] = useState<ShadowRoot>()
const [cacheNode, setCacheNode] = useState<EmotionCache>()
const [config, setConfig] = useState<ViewModel>()
useEffect(() => {
if (!nodeRef.current) {
return
}
const root = nodeRef.current.attachShadow({ mode: 'open' })
setRootNode(root)
setCacheNode(
createCache({ key: 'react-shadow', prepend: true, container: root }),
)
const engine = createViewState({
assembly: {
name: 'volvox',
uri: 'https://jbrowse.org/genomes/volvox/volvox.2bit',
},
tracks: [
{
type: 'FeatureTrack',
trackId: 'volvox_gff3',
name: 'Volvox genes',
assemblyNames: ['volvox'],
adapter: {
type: 'Gff3TabixAdapter',
uri: 'https://jbrowse.org/code/jb2/main/test_data/volvox/volvox.sort.gff3.gz',
},
},
],
location: 'ctgA:1105..1221',
configuration: {
theme: {
palette: { primary: { main: '#4400a6' } },
components: {
MuiPopover: {
defaultProps: { container: () => nodeForPinRef.current },
},
MuiPopper: {
defaultProps: { container: () => nodeForPinRef.current },
},
MuiTooltip: {
defaultProps: {
slotProps: {
popper: { container: () => nodeForPinRef.current },
},
},
},
MuiModal: {
defaultProps: { container: () => nodeForPinRef.current },
},
MuiMenu: {
defaultProps: { container: () => nodeForPinRef.current },
},
},
},
},
})
setConfig(engine)
return () => {
destroyViewState(engine)
}
}, [])
return (
<div ref={nodeRef}>
{rootNode && config && cacheNode
? createPortal(
<CacheProvider value={cacheNode}>
<JBrowseLinearGenomeView viewState={config} />
<div ref={nodeForPinRef} />
</CacheProvider>,
rootNode,
)
: null}
</div>
)
}
const JBrowseCustom = () => createElement(ShadowComponent, null, null)
export default function ShadowDOMOneLinearGenomeView() {
if (customElements.get('jbrowse-linear-view') === undefined) {
customElements.define('jbrowse-linear-view', r2wc(JBrowseCustom))
}
return createElement('jbrowse-linear-view')
}