diff --git a/src/mol-app/component/color-theme.tsx b/src/mol-app/component/color-theme.tsx
index e546c25bd4eaafd8da71102d14d122bf5a5b6c16..bf3c58078c39ac4674b18d09dd7be1a865c705f2 100644
--- a/src/mol-app/component/color-theme.tsx
+++ b/src/mol-app/component/color-theme.tsx
@@ -9,7 +9,7 @@ import { ColorTheme } from 'mol-theme/color';
 import { Color } from 'mol-util/color';
 
 export interface ColorThemeComponentProps {
-    colorTheme: ColorTheme
+    colorTheme: ColorTheme<any>
 }
 
 export interface ColorThemeComponentState {
diff --git a/src/mol-geo/geometry/color-data.ts b/src/mol-geo/geometry/color-data.ts
index 43c2bd3109c284ce351ef9f04a1a6b253ea6c310..1f4fb376a56cf1749fc400a2d2b99cb24d384661 100644
--- a/src/mol-geo/geometry/color-data.ts
+++ b/src/mol-geo/geometry/color-data.ts
@@ -22,7 +22,7 @@ export type ColorData = {
     dColorType: ValueCell<string>,
 }
 
-export function createColors(locationIt: LocationIterator, colorTheme: ColorTheme, colorData?: ColorData): ColorData {
+export function createColors(locationIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
     switch (getGranularity(locationIt, colorTheme.granularity)) {
         case 'uniform': return createUniformColor(locationIt, colorTheme.color, colorData)
         case 'group': return createGroupColor(locationIt, colorTheme.color, colorData)
diff --git a/src/mol-geo/geometry/size-data.ts b/src/mol-geo/geometry/size-data.ts
index a1651cc4b101448934fc03aeed09779377b98855..6cb0e8b755d5be410438441fff5ef7f8e46e9e04 100644
--- a/src/mol-geo/geometry/size-data.ts
+++ b/src/mol-geo/geometry/size-data.ts
@@ -21,7 +21,7 @@ export type SizeData = {
     dSizeType: ValueCell<string>,
 }
 
-export function createSizes(locationIt: LocationIterator, sizeTheme: SizeTheme, sizeData?: SizeData): SizeData {
+export function createSizes(locationIt: LocationIterator, sizeTheme: SizeTheme<any>, sizeData?: SizeData): SizeData {
     switch (getGranularity(locationIt, sizeTheme.granularity)) {
         case 'uniform': return createUniformSize(locationIt, sizeTheme.size, sizeData)
         case 'group': return createGroupSize(locationIt, sizeTheme.size, sizeData)
diff --git a/src/mol-model-props/rcsb/themes/assembly-symmetry-cluster.ts b/src/mol-model-props/rcsb/themes/assembly-symmetry-cluster.ts
index 7bfbff51a3c7105a2d45bef7cfd76d8a7db90c50..d739f926c065dc8969bd1833f4e696d45de133af 100644
--- a/src/mol-model-props/rcsb/themes/assembly-symmetry-cluster.ts
+++ b/src/mol-model-props/rcsb/themes/assembly-symmetry-cluster.ts
@@ -113,5 +113,6 @@ export const AssemblySymmetryClusterColorThemeProvider: ColorTheme.Provider<Asse
     label: 'RCSB Assembly Symmetry Cluster',
     factory: AssemblySymmetryClusterColorTheme,
     getParams: getAssemblySymmetryClusterColorThemeParams,
-    defaultValues: PD.getDefaultValues(AssemblySymmetryClusterColorThemeParams)
+    defaultValues: PD.getDefaultValues(AssemblySymmetryClusterColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ctx.structure.models[0].customProperties.has(AssemblySymmetry.Descriptor)
 }
\ No newline at end of file
diff --git a/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts b/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts
index 948edad6cfe81e0a08819b3b10cef2c7b2be568b..61a09e969723d1c0d28ece8064cee69b88f11b95 100644
--- a/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts
+++ b/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts
@@ -12,6 +12,7 @@ import { StructureElement } from 'mol-model/structure';
 import { CustomPropertyRegistry } from 'mol-plugin/util/custom-prop-registry';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { PluginBehavior } from '../../../behavior';
+import { ThemeDataContext } from 'mol-theme/theme';
 
 export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: boolean }>({
     name: 'pdbe-structure-quality-report-prop',
@@ -34,13 +35,12 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
             this.ctx.customModelProperties.register(this.provider);
             this.ctx.lociLabels.addProvider(labelPDBeValidation);
 
-            // TODO: support filtering of themes based on the input structure
-            // in this case, it would check structure.models[0].customProperties.has(StructureQualityReport.Descriptor)
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add('pdbe-structure-quality-report', {
                 label: 'PDBe Structure Quality Report',
                 factory: StructureQualityReportColorTheme,
                 getParams: () => ({}),
-                defaultValues: {}
+                defaultValues: {},
+                isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ctx.structure.models[0].customProperties.has(StructureQualityReport.Descriptor)
             })
         }
 
diff --git a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts
index e7c7ce4f56bcb76775d47642e46b3c4a50a9eb60..5fac2530910d894c4df21d9fd33e380737dfd2ae 100644
--- a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts
+++ b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts
@@ -31,9 +31,6 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean
         register(): void {
             this.ctx.customModelProperties.register(this.provider);
             this.ctx.lociLabels.addProvider(labelAssemblySymmetryAxes);
-
-            // TODO: support filtering of themes and representations based on the input structure
-            // in this case, it would check structure.models[0].customProperties.has(AssemblySymmetry.Descriptor)
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add('rcsb-assembly-symmetry-cluster', AssemblySymmetryClusterColorThemeProvider)
             this.ctx.structureRepresentation.registry.add('rcsb-assembly-symmetry-axes', AssemblySymmetryAxesRepresentationProvider)
         }
diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts
index 588fc0dc6212d1502a7db970819c1ff4ccc2b7f7..069805cb01d623126d81116f43fd632e0449076e 100644
--- a/src/mol-plugin/context.ts
+++ b/src/mol-plugin/context.ts
@@ -73,7 +73,7 @@ export class PluginContext {
 
     readonly structureRepresentation = {
         registry: new StructureRepresentationRegistry(),
-        themeCtx: { colorThemeRegistry: new ColorTheme.Registry(), sizeThemeRegistry: new SizeTheme.Registry() } as ThemeRegistryContext
+        themeCtx: { colorThemeRegistry: ColorTheme.createRegistry(), sizeThemeRegistry: SizeTheme.createRegistry() } as ThemeRegistryContext
     }
 
     readonly customModelProperties = new CustomPropertyRegistry();
diff --git a/src/mol-plugin/state/transforms/representation.ts b/src/mol-plugin/state/transforms/representation.ts
index 443148667a10e9acc946c16eb85d9a53cdf230ec..942816e5e0463b49aef7da8014af37a51ca69958 100644
--- a/src/mol-plugin/state/transforms/representation.ts
+++ b/src/mol-plugin/state/transforms/representation.ts
@@ -50,21 +50,23 @@ const StructureRepresentation3D = PluginStateTransform.BuiltIn({
     from: SO.Molecule.Structure,
     to: SO.Molecule.Representation3D,
     params: (a, ctx: PluginContext) => {
-        const type = ctx.structureRepresentation.registry.get(ctx.structureRepresentation.registry.default.name);
+        const { registry, themeCtx } = ctx.structureRepresentation
+        const type = registry.get(registry.default.name);
+        const dataCtx = { structure: a.data }
         return ({
             type: PD.Mapped<any>(
-                ctx.structureRepresentation.registry.default.name,
-                ctx.structureRepresentation.registry.types,
-                name => PD.Group<any>(ctx.structureRepresentation.registry.get(name).getParams(ctx.structureRepresentation.themeCtx, a.data))),
+                registry.default.name,
+                registry.types,
+                name => PD.Group<any>(registry.get(name).getParams(themeCtx, a.data))),
             colorTheme: PD.Mapped<any>(
                 type.defaultColorTheme,
-                ctx.structureRepresentation.themeCtx.colorThemeRegistry.types,
-                name => PD.Group<any>(ctx.structureRepresentation.themeCtx.colorThemeRegistry.get(name).getParams({ structure: a.data }))
+                themeCtx.colorThemeRegistry.getApplicableTypes(dataCtx),
+                name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams(dataCtx))
             ),
             sizeTheme: PD.Mapped<any>(
                 type.defaultSizeTheme,
-                ctx.structureRepresentation.themeCtx.sizeThemeRegistry.types,
-                name => PD.Group<any>(ctx.structureRepresentation.themeCtx.sizeThemeRegistry.get(name).getParams({ structure: a.data }))
+                themeCtx.sizeThemeRegistry.types,
+                name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams(dataCtx))
             )
         })
     }
diff --git a/src/mol-theme/color.ts b/src/mol-theme/color.ts
index 75bbc2eee2c4b815bc5be4407d2fb7df229e2c10..ba17219d3f3ed1d39ed855a0e5188ef2ba811311 100644
--- a/src/mol-theme/color.ts
+++ b/src/mol-theme/color.ts
@@ -11,7 +11,7 @@ import { CarbohydrateSymbolColorThemeProvider } from './color/carbohydrate-symbo
 import { UniformColorThemeProvider } from './color/uniform';
 import { deepEqual } from 'mol-util';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { ThemeDataContext } from './theme';
+import { ThemeDataContext, ThemeRegistry, ThemeProvider } from './theme';
 import { ChainIdColorThemeProvider } from './color/chain-id';
 import { CrossLinkColorThemeProvider } from './color/cross-link';
 import { ElementIndexColorThemeProvider } from './color/element-index';
@@ -32,7 +32,7 @@ export type LocationColor = (location: Location, isSecondary: boolean) => Color
 export type ColorThemeProps = { [k: string]: any }
 
 export { ColorTheme }
-interface ColorTheme<P extends PD.Params = {}> {
+interface ColorTheme<P extends PD.Params> {
     readonly factory: ColorTheme.Factory<P>
     readonly granularity: ColorType
     readonly color: LocationColor
@@ -47,56 +47,16 @@ namespace ColorTheme {
     const EmptyColor = Color(0xCCCCCC)
     export const Empty: ColorTheme<{}> = { factory: EmptyFactory, granularity: 'uniform', color: () => EmptyColor, props: {} }
 
-    export function areEqual(themeA: ColorTheme, themeB: ColorTheme) {
+    export function areEqual(themeA: ColorTheme<any>, themeB: ColorTheme<any>) {
         return themeA.factory === themeB.factory && deepEqual(themeA.props, themeB.props)
     }
 
-    export interface Provider<P extends PD.Params> {
-        readonly label: string
-        readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => ColorTheme<P>
-        readonly getParams: (ctx: ThemeDataContext) => P
-        readonly defaultValues: PD.Values<P>
-    }
-    export const EmptyProvider: Provider<{}> = { label: '', factory: EmptyFactory, getParams: () => ({}), defaultValues: {} }
-
-    export class Registry {
-        private _list: { name: string, provider: Provider<any> }[] = []
-        private _map = new Map<string, Provider<any>>()
-
-        get default() { return this._list[0]; }
-        get types(): [string, string][] {
-            return this._list.map(e => [e.name, e.provider.label] as [string, string]);
-        }
-
-        constructor() {
-            Object.keys(BuiltInColorThemes).forEach(name => {
-                const p = (BuiltInColorThemes as { [k: string]: Provider<any> })[name]
-                this.add(name, p)
-            })
-        }
-
-        add<P extends PD.Params>(name: string, provider: Provider<P>) {
-            this._list.push({ name, provider })
-            this._map.set(name, provider)
-        }
-
-        remove(name: string) {
-            this._list.splice(this._list.findIndex(e => e.name === name))
-            this._map.delete(name)
-        }
-
-        get<P extends PD.Params>(name: string): Provider<P> {
-            return this._map.get(name) || EmptyProvider as unknown as Provider<P>
-        }
-
-        create(name: string, ctx: ThemeDataContext, props = {}) {
-            const provider = this.get(name)
-            return provider.factory(ctx, { ...PD.getDefaultValues(provider.getParams(ctx)), ...props })
-        }
+    export interface Provider<P extends PD.Params> extends ThemeProvider<ColorTheme<P>, P> { }
+    export const EmptyProvider: Provider<{}> = { label: '', factory: EmptyFactory, getParams: () => ({}), defaultValues: {}, isApplicable: () => true }
 
-        get list() {
-            return this._list
-        }
+    export type Registry = ThemeRegistry<ColorTheme<any>>
+    export function createRegistry() {
+        return new ThemeRegistry(BuiltInColorThemes as { [k: string]: Provider<any> }, EmptyProvider)
     }
 }
 
diff --git a/src/mol-theme/color/carbohydrate-symbol.ts b/src/mol-theme/color/carbohydrate-symbol.ts
index 550c8f35d7799542fb62eba8903f7f1fdf5e50f2..72c7382c1917c3da8cda266d395ccec0e478e688 100644
--- a/src/mol-theme/color/carbohydrate-symbol.ts
+++ b/src/mol-theme/color/carbohydrate-symbol.ts
@@ -78,5 +78,6 @@ export const CarbohydrateSymbolColorThemeProvider: ColorTheme.Provider<Carbohydr
     label: 'Carbohydrate Symbol',
     factory: CarbohydrateSymbolColorTheme,
     getParams: getCarbohydrateSymbolColorThemeParams,
-    defaultValues: PD.getDefaultValues(CarbohydrateSymbolColorThemeParams)
+    defaultValues: PD.getDefaultValues(CarbohydrateSymbolColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/chain-id.ts b/src/mol-theme/color/chain-id.ts
index eb37bf31c59088815d2d7401d5ef14645601c14e..c89d0600260ec58d9855c8317cf2c5f251d099f8 100644
--- a/src/mol-theme/color/chain-id.ts
+++ b/src/mol-theme/color/chain-id.ts
@@ -95,5 +95,6 @@ export const ChainIdColorThemeProvider: ColorTheme.Provider<ChainIdColorThemePar
     label: 'Chain Id',
     factory: ChainIdColorTheme,
     getParams: getChainIdColorThemeParams,
-    defaultValues: PD.getDefaultValues(ChainIdColorThemeParams)
+    defaultValues: PD.getDefaultValues(ChainIdColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/cross-link.ts b/src/mol-theme/color/cross-link.ts
index bfe6cc0acb1bf17374e3d745b804d63dfb3c4859..d3cdf4b806a4c22633b3c13213cb7bef2b8712ea 100644
--- a/src/mol-theme/color/cross-link.ts
+++ b/src/mol-theme/color/cross-link.ts
@@ -72,5 +72,6 @@ export const CrossLinkColorThemeProvider: ColorTheme.Provider<CrossLinkColorThem
     label: 'Cross Link',
     factory: CrossLinkColorTheme,
     getParams: getCrossLinkColorThemeParams,
-    defaultValues: PD.getDefaultValues(CrossLinkColorThemeParams)
+    defaultValues: PD.getDefaultValues(CrossLinkColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/element-index.ts b/src/mol-theme/color/element-index.ts
index c650f6c99f5847e7965840b431dbc08411571220..3636026ab977035052e72b019f6b24dc0adf83a1 100644
--- a/src/mol-theme/color/element-index.ts
+++ b/src/mol-theme/color/element-index.ts
@@ -72,5 +72,6 @@ export const ElementIndexColorThemeProvider: ColorTheme.Provider<ElementIndexCol
     label: 'Element Index',
     factory: ElementIndexColorTheme,
     getParams: getElementIndexColorThemeParams,
-    defaultValues: PD.getDefaultValues(ElementIndexColorThemeParams)
+    defaultValues: PD.getDefaultValues(ElementIndexColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/element-symbol.ts b/src/mol-theme/color/element-symbol.ts
index bc15c9f232aedca3b9334eb6cd3f6751b2ba4438..28055bab2c48b626b43dbf4c615500674d119307 100644
--- a/src/mol-theme/color/element-symbol.ts
+++ b/src/mol-theme/color/element-symbol.ts
@@ -64,5 +64,6 @@ export const ElementSymbolColorThemeProvider: ColorTheme.Provider<ElementSymbolC
     label: 'Element Symbol',
     factory: ElementSymbolColorTheme,
     getParams: getElementSymbolColorThemeParams,
-    defaultValues: PD.getDefaultValues(ElementSymbolColorThemeParams)
+    defaultValues: PD.getDefaultValues(ElementSymbolColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/molecule-type.ts b/src/mol-theme/color/molecule-type.ts
index b91c557afd98c6abb6dde170ad0a6ee4519b7aee..ad643930c68e537ff08231c8cd378f7d1b3d6f59 100644
--- a/src/mol-theme/color/molecule-type.ts
+++ b/src/mol-theme/color/molecule-type.ts
@@ -73,5 +73,6 @@ export const MoleculeTypeColorThemeProvider: ColorTheme.Provider<MoleculeTypeCol
     label: 'Molecule Type',
     factory: MoleculeTypeColorTheme,
     getParams: getMoleculeTypeColorThemeParams,
-    defaultValues: PD.getDefaultValues(MoleculeTypeColorThemeParams)
+    defaultValues: PD.getDefaultValues(MoleculeTypeColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/polymer-id.ts b/src/mol-theme/color/polymer-id.ts
index 41b92030a827d5ab17c10d122a9c66370a6bf623..87c1c75aef281c170ca71dee0765f5ea8f6b80cd 100644
--- a/src/mol-theme/color/polymer-id.ts
+++ b/src/mol-theme/color/polymer-id.ts
@@ -102,5 +102,6 @@ export const PolymerIdColorThemeProvider: ColorTheme.Provider<PolymerIdColorThem
     label: 'Polymer Id',
     factory: PolymerIdColorTheme,
     getParams: getPolymerIdColorThemeParams,
-    defaultValues: PD.getDefaultValues(PolymerIdColorThemeParams)
+    defaultValues: PD.getDefaultValues(PolymerIdColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/polymer-index.ts b/src/mol-theme/color/polymer-index.ts
index a51257fc888bfbc14189a5f8a42aaa398419354e..d13fe8d175cd249eaad5cea8c25ad3cdf6c6b3c0 100644
--- a/src/mol-theme/color/polymer-index.ts
+++ b/src/mol-theme/color/polymer-index.ts
@@ -70,5 +70,6 @@ export const PolymerIndexColorThemeProvider: ColorTheme.Provider<PolymerIndexCol
     label: 'Polymer Index',
     factory: PolymerIndexColorTheme,
     getParams: getPolymerIndexColorThemeParams,
-    defaultValues: PD.getDefaultValues(PolymerIndexColorThemeParams)
+    defaultValues: PD.getDefaultValues(PolymerIndexColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/residue-name.ts b/src/mol-theme/color/residue-name.ts
index 682ae8a4bbe2aebbae81f9d5002a7ed6c66c7604..999bf2a9b274bc9369be3625cf7d7daa895b0a6d 100644
--- a/src/mol-theme/color/residue-name.ts
+++ b/src/mol-theme/color/residue-name.ts
@@ -129,5 +129,6 @@ export const ResidueNameColorThemeProvider: ColorTheme.Provider<ResidueNameColor
     label: 'Residue Name',
     factory: ResidueNameColorTheme,
     getParams: getResidueNameColorThemeParams,
-    defaultValues: PD.getDefaultValues(ResidueNameColorThemeParams)
+    defaultValues: PD.getDefaultValues(ResidueNameColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/secondary-structure.ts b/src/mol-theme/color/secondary-structure.ts
index 6f2d094feecf2db20216fea84f4bc134997029bd..384c5fb0b7d3c37031ef670b0d17a9e7b9f79ade 100644
--- a/src/mol-theme/color/secondary-structure.ts
+++ b/src/mol-theme/color/secondary-structure.ts
@@ -96,5 +96,6 @@ export const SecondaryStructureColorThemeProvider: ColorTheme.Provider<Secondary
     label: 'Secondary Structure',
     factory: SecondaryStructureColorTheme,
     getParams: getSecondaryStructureColorThemeParams,
-    defaultValues: PD.getDefaultValues(SecondaryStructureColorThemeParams)
+    defaultValues: PD.getDefaultValues(SecondaryStructureColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/sequence-id.ts b/src/mol-theme/color/sequence-id.ts
index cb8e49ffaacbfd888b3597cfb959c9c84dce9d83..b0dd084b66297adc2c42259e73a118cec3102283 100644
--- a/src/mol-theme/color/sequence-id.ts
+++ b/src/mol-theme/color/sequence-id.ts
@@ -103,5 +103,6 @@ export const SequenceIdColorThemeProvider: ColorTheme.Provider<SequenceIdColorTh
     label: 'Sequence Id',
     factory: SequenceIdColorTheme,
     getParams: getSequenceIdColorThemeParams,
-    defaultValues: PD.getDefaultValues(SequenceIdColorThemeParams)
+    defaultValues: PD.getDefaultValues(SequenceIdColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/shape-group.ts b/src/mol-theme/color/shape-group.ts
index b6cbba700b95935cda88dcd0ae6f4a393f2cbfe4..66c51e853aa7eb81ddece5f6fed64e74188c3bde 100644
--- a/src/mol-theme/color/shape-group.ts
+++ b/src/mol-theme/color/shape-group.ts
@@ -39,5 +39,6 @@ export const ShapeGroupColorThemeProvider: ColorTheme.Provider<ShapeGroupColorTh
     label: 'Shape Group',
     factory: ShapeGroupColorTheme,
     getParams: getShapeGroupColorThemeParams,
-    defaultValues: PD.getDefaultValues(ShapeGroupColorThemeParams)
+    defaultValues: PD.getDefaultValues(ShapeGroupColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.shape
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/uniform.ts b/src/mol-theme/color/uniform.ts
index 8c702a9989c01c2bed0ebcd2e19068fc21a91a54..c18b62c9afaf9b1f436d22bab4397eeabae0e68e 100644
--- a/src/mol-theme/color/uniform.ts
+++ b/src/mol-theme/color/uniform.ts
@@ -38,5 +38,6 @@ export const UniformColorThemeProvider: ColorTheme.Provider<UniformColorThemePar
     label: 'Uniform',
     factory: UniformColorTheme,
     getParams: getUniformColorThemeParams,
-    defaultValues: PD.getDefaultValues(UniformColorThemeParams)
+    defaultValues: PD.getDefaultValues(UniformColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => true
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/unit-index.ts b/src/mol-theme/color/unit-index.ts
index 4f9d14883d8cb91649250617549a839ed390970b..ac25d51137c1cc75d40afcbd15def80d3876d63d 100644
--- a/src/mol-theme/color/unit-index.ts
+++ b/src/mol-theme/color/unit-index.ts
@@ -61,5 +61,6 @@ export const UnitIndexColorThemeProvider: ColorTheme.Provider<UnitIndexColorThem
     label: 'Unit Index',
     factory: UnitIndexColorTheme,
     getParams: getUnitIndexColorThemeParams,
-    defaultValues: PD.getDefaultValues(UnitIndexColorThemeParams)
+    defaultValues: PD.getDefaultValues(UnitIndexColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
 }
\ No newline at end of file
diff --git a/src/mol-theme/size.ts b/src/mol-theme/size.ts
index 16179eb0d6462a413625d7ac0eecdf3092fe977e..11dd2158880ad7e64bbb34e211da8f5c35836aad 100644
--- a/src/mol-theme/size.ts
+++ b/src/mol-theme/size.ts
@@ -7,12 +7,12 @@
 import { SizeType, LocationSize } from 'mol-geo/geometry/size-data';
 import { UniformSizeThemeProvider } from './size/uniform';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { ThemeDataContext } from 'mol-theme/theme';
+import { ThemeDataContext, ThemeRegistry, ThemeProvider } from 'mol-theme/theme';
 import { PhysicalSizeThemeProvider } from './size/physical';
 import { deepEqual } from 'mol-util';
 
 export { SizeTheme }
-interface SizeTheme<P extends PD.Params = {}> {
+interface SizeTheme<P extends PD.Params> {
     readonly factory: SizeTheme.Factory<P>
     readonly granularity: SizeType
     readonly size: LocationSize
@@ -25,56 +25,16 @@ namespace SizeTheme {
     export const EmptyFactory = () => Empty
     export const Empty: SizeTheme<{}> = { factory: EmptyFactory, granularity: 'uniform', size: () => 1, props: {} }
 
-    export function areEqual(themeA: SizeTheme, themeB: SizeTheme) {
+    export function areEqual(themeA: SizeTheme<any>, themeB: SizeTheme<any>) {
         return themeA.factory === themeB.factory && deepEqual(themeA.props, themeB.props)
     }
 
-    export interface Provider<P extends PD.Params> {
-        readonly label: string
-        readonly factory: Factory<P>
-        readonly getParams: (ctx: ThemeDataContext) => P
-        readonly defaultValues: PD.Values<P>
-    }
-    export const EmptyProvider: Provider<{}> = { label: '', factory: EmptyFactory, getParams: () => ({}), defaultValues: {} }
-
-    export class Registry {
-        private _list: { name: string, provider: Provider<any> }[] = []
-        private _map = new Map<string, Provider<any>>()
-
-        get default() { return this._list[0]; }
-        get types(): [string, string][] {
-            return this._list.map(e => [e.name, e.provider.label] as [string, string]);
-        }
-
-        constructor() {
-            Object.keys(BuiltInSizeThemes).forEach(name => {
-                const p = (BuiltInSizeThemes as { [k: string]: Provider<any> })[name]
-                this.add(name, p)
-            })
-        }
-
-        add<P extends PD.Params>(name: string, provider: Provider<P>) {
-            this._list.push({ name, provider })
-            this._map.set(name, provider)
-        }
-
-        remove(name: string) {
-            this._list.splice(this._list.findIndex(e => e.name === name))
-            this._map.delete(name)
-        }
-
-        get<P extends PD.Params>(name: string): Provider<P> {
-            return this._map.get(name) || EmptyProvider as unknown as Provider<P>
-        }
-
-        create(name: string, ctx: ThemeDataContext, props = {}) {
-            const provider = this.get(name)
-            return provider.factory(ctx, { ...PD.getDefaultValues(provider.getParams(ctx)), ...props })
-        }
+    export interface Provider<P extends PD.Params> extends ThemeProvider<SizeTheme<P>, P> { }
+    export const EmptyProvider: Provider<{}> = { label: '', factory: EmptyFactory, getParams: () => ({}), defaultValues: {}, isApplicable: () => true }
 
-        get list() {
-            return this._list
-        }
+    export type Registry = ThemeRegistry<SizeTheme<any>>
+    export function createRegistry() {
+        return new ThemeRegistry(BuiltInSizeThemes as { [k: string]: Provider<any> }, EmptyProvider)
     }
 }
 
diff --git a/src/mol-theme/size/physical.ts b/src/mol-theme/size/physical.ts
index b856912c418d09ae35c8e6aca966378b9381d30d..3d141c045e72f9699af758be2fb56501855d48a6 100644
--- a/src/mol-theme/size/physical.ts
+++ b/src/mol-theme/size/physical.ts
@@ -60,5 +60,6 @@ export const PhysicalSizeThemeProvider: SizeTheme.Provider<PhysicalSizeThemePara
     label: 'Physical',
     factory: PhysicalSizeTheme,
     getParams: getPhysicalSizeThemeParams,
-    defaultValues: PD.getDefaultValues(PhysicalSizeThemeParams)
+    defaultValues: PD.getDefaultValues(PhysicalSizeThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
 }
\ No newline at end of file
diff --git a/src/mol-theme/size/uniform.ts b/src/mol-theme/size/uniform.ts
index 3d91d0c353985aeb0a5b5baedf15b7b93f775cee..69c7fd832889abd3f869f2770ad7bfba58525997 100644
--- a/src/mol-theme/size/uniform.ts
+++ b/src/mol-theme/size/uniform.ts
@@ -34,5 +34,6 @@ export const UniformSizeThemeProvider: SizeTheme.Provider<UniformSizeThemeParams
     label: 'Uniform',
     factory: UniformSizeTheme,
     getParams: getUniformSizeThemeParams,
-    defaultValues: PD.getDefaultValues(UniformSizeThemeParams)
+    defaultValues: PD.getDefaultValues(UniformSizeThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => true
 }
\ No newline at end of file
diff --git a/src/mol-theme/theme.ts b/src/mol-theme/theme.ts
index ad2222c388cba75c6509875228e11e6c5226afc3..7808942b8a2700939012c553f0b2c8cb2daa4a45 100644
--- a/src/mol-theme/theme.ts
+++ b/src/mol-theme/theme.ts
@@ -9,6 +9,7 @@ import { SizeTheme } from './size';
 import { Structure } from 'mol-model/structure';
 import { VolumeData } from 'mol-model/volume';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
+import { Shape } from 'mol-model/shape';
 
 export interface ThemeRegistryContext {
     colorThemeRegistry: ColorTheme.Registry
@@ -19,11 +20,12 @@ export interface ThemeDataContext {
     [k: string]: any
     structure?: Structure
     volume?: VolumeData
+    shape?: Shape
 }
 
 export interface Theme {
-    color: ColorTheme
-    size: SizeTheme
+    color: ColorTheme<any>
+    size: SizeTheme<any>
     // label: LabelTheme // TODO
 }
 
@@ -43,4 +45,59 @@ export function createTheme(ctx: ThemeRegistryContext, data: ThemeDataContext, p
 
 export function createEmptyTheme(): Theme {
     return { color: ColorTheme.Empty, size: SizeTheme.Empty }
+}
+
+//
+
+export interface ThemeProvider<T extends ColorTheme<P> | SizeTheme<P>, P extends PD.Params> {
+    readonly label: string
+    readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => T
+    readonly getParams: (ctx: ThemeDataContext) => P
+    readonly defaultValues: PD.Values<P>
+    readonly isApplicable: (ctx: ThemeDataContext) => boolean
+}
+
+function getTypes(list: { name: string, provider: ThemeProvider<any, any> }[]) {
+    return list.map(e => [e.name, e.provider.label] as [string, string]);
+}
+
+export class ThemeRegistry<T extends ColorTheme<any> | SizeTheme<any>> {
+    private _list: { name: string, provider: ThemeProvider<T, any> }[] = []
+    private _map = new Map<string, ThemeProvider<T, any>>()
+
+    get default() { return this._list[0]; }
+    get list() { return this._list }
+    get types(): [string, string][] { return getTypes(this._list) }
+
+    constructor(builtInThemes: { [k: string]: ThemeProvider<T, any> }, private emptyProvider: ThemeProvider<T, any>) {
+        Object.keys(builtInThemes).forEach(name => this.add(name, builtInThemes[name]))
+    }
+
+    add<P extends PD.Params>(name: string, provider: ThemeProvider<T, P>) {
+        this._list.push({ name, provider })
+        this._map.set(name, provider)
+    }
+
+    remove(name: string) {
+        this._list.splice(this._list.findIndex(e => e.name === name))
+        this._map.delete(name)
+        console.log('removed', name, this._list, this._map)
+    }
+
+    get<P extends PD.Params>(name: string): ThemeProvider<T, P> {
+        return this._map.get(name) || this.emptyProvider
+    }
+
+    create(name: string, ctx: ThemeDataContext, props = {}) {
+        const provider = this.get(name)
+        return provider.factory(ctx, { ...PD.getDefaultValues(provider.getParams(ctx)), ...props })
+    }
+
+    getApplicableList(ctx: ThemeDataContext) {
+        return this._list.filter(e => e.provider.isApplicable(ctx));
+    }
+
+    getApplicableTypes(ctx: ThemeDataContext) {
+        return getTypes(this.getApplicableList(ctx))
+    }
 }
\ No newline at end of file