diff --git a/angular.json b/angular.json
index 3964720d4694e01f3a452109d5e6e53d2445a850..af94adeb567bf3ab3406beeba94e8191e7e97c11 100644
--- a/angular.json
+++ b/angular.json
@@ -10,7 +10,7 @@
       "projectType": "application",
       "schematics": {
         "@schematics/angular:component": {
-          "style": "sass"
+          "style": "scss"
         },
         "@schematics/angular:application": {
           "strict": true
@@ -18,7 +18,7 @@
       },
       "root": "",
       "sourceRoot": "src",
-      "prefix": "app",
+      "prefix": "sxplr",
       "architect": {
         "build": {
           "builder": "@angular-devkit/build-angular:browser",
diff --git a/src/atlasComponents/sapi/core/base.ts b/src/atlasComponents/sapi/core/base.ts
index 9783082a284974c593798f1e05cd11cf0d17b0e0..2bfaa27d605cb05633277602e9fdf5dc870c8bc5 100644
--- a/src/atlasComponents/sapi/core/base.ts
+++ b/src/atlasComponents/sapi/core/base.ts
@@ -4,13 +4,12 @@ import { RouteParam } from "../typeV3"
 import { SapiQueryPriorityArg } from "../sxplrTypes"
 
 const AllFeatures = {
-  // Feature: "Feature",
   CorticalProfile: "CorticalProfile",
   // EbrainsDataFeature: "EbrainsDataFeature",
   RegionalConnectivity: "RegionalConnectivity",
   Tabular: "Tabular",
-  GeneExpressions: "GeneExpressions",
-  VolumeOfInterest: "VolumeOfInterest",
+  // GeneExpressions: "GeneExpressions",
+  Image: "Image",
 } as const
 
 type AF = keyof typeof AllFeatures
diff --git a/src/atlasComponents/sapi/core/sapiSpace.ts b/src/atlasComponents/sapi/core/sapiSpace.ts
index e1826a6d97d116f23a46af1378c344d1aa56d5d0..c6e8741eaf2be6adf9b1a5e9af540c7ec33a0a1c 100644
--- a/src/atlasComponents/sapi/core/sapiSpace.ts
+++ b/src/atlasComponents/sapi/core/sapiSpace.ts
@@ -8,7 +8,7 @@ import { SAPIBase } from "./base"
  * All valid parcellation features
  */
 const SpaceFeatures = {
-  VolumeOfInterest: "VolumeOfInterest",
+  Image: "Image",
 } as const
 
 export type SF = keyof typeof SpaceFeatures
diff --git a/src/atlasComponents/sapi/sapi.service.ts b/src/atlasComponents/sapi/sapi.service.ts
index 924fce5ce1c7181ac2b7e5fdaf54b00a05aacb5d..fb51ddd60f159234296d393e7f0bf65fdc209d5a 100644
--- a/src/atlasComponents/sapi/sapi.service.ts
+++ b/src/atlasComponents/sapi/sapi.service.ts
@@ -12,7 +12,7 @@ import { PRIORITY_HEADER } from "src/util/priority";
 import { forkJoin, from, NEVER, Observable, of, throwError } from "rxjs";
 import { SAPIFeature } from "./features";
 import { environment } from "src/environments/environment"
-import { PathReturn, RouteParam, SapiRoute } from "./typeV3";
+import { FeatureType, PathReturn, RouteParam, SapiRoute } from "./typeV3";
 import { BoundingBox, SxplrAtlas, SxplrParcellation, SxplrRegion, SxplrTemplate, VoiFeature, SapiQueryPriorityArg } from "./sxplrTypes";
 
 
@@ -173,6 +173,43 @@ export class SAPI{
       }
     })
   }
+
+  #isPaged<T>(resp: any): resp is PaginatedResponse<T>{
+    if (!!resp.total) return true
+    return false
+  }
+  getV3Features<T extends FeatureType>(featureType: T, sapiParam: RouteParam<`/feature/${T}`>): Observable<PathReturn<`/feature/${T}/{feature_id}`>[]> {
+    const query = structuredClone(sapiParam)
+    return this.v3Get<`/feature/${T}`>(`/feature/${featureType}`, {
+      ...query
+    }).pipe(
+      switchMap(resp => {
+        if (!this.#isPaged(resp)) return throwError(`endpoint not returning paginated response`)
+        return this.iteratePages(
+          resp,
+          page => {
+            const query = structuredClone(sapiParam)
+            query.query.page = page
+            return this.v3Get(`/feature/${featureType}`, {
+              ...query,
+            }).pipe(
+              map(val => {
+                if (this.#isPaged(val)) return val
+                return { items: [], total: 0, page: 0, size: 0 }
+              })
+            )
+          }
+        )
+      }),
+      catchError(() => of([]))
+    )
+  }
+
+  getV3FeatureDetail<T extends FeatureType>(featureType: T, sapiParam: RouteParam<`/feature/${T}/{feature_id}`>): Observable<PathReturn<`/feature/${T}/{feature_id}`>> {
+    return this.v3Get<`/feature/${T}/{feature_id}`>(`/feature/${featureType}/{feature_id}`, {
+      ...sapiParam
+    })
+  }
   
   getFeature(featureId: string, opts: Record<string, string> = {}) {
     return new SAPIFeature(this, featureId, opts)
@@ -257,7 +294,6 @@ export class SAPI{
     }),
     shareReplay(1),
   )
-    
 
   public getAllSpaces(atlas: SxplrAtlas): Observable<SxplrTemplate[]> {
     return forkJoin(
@@ -391,7 +427,7 @@ export class SAPI{
     /**
      * FIXME iterate over all pages
      */
-    return this.v3Get("/feature/VolumeOfInterest", {
+    return this.v3Get("/feature/Image", {
       query: {
         space_id: bbox.space.id,
         bbox: JSON.stringify([bbox.minpoint, bbox.maxpoint]),
diff --git a/src/atlasComponents/sapi/schemaV3.ts b/src/atlasComponents/sapi/schemaV3.ts
index 7147fd8c7a5b5c5511895b02e296518108ac20d1..98036ee8f5e613ec49583d5d79cce51f1f35db6d 100644
--- a/src/atlasComponents/sapi/schemaV3.ts
+++ b/src/atlasComponents/sapi/schemaV3.ts
@@ -42,11 +42,23 @@ export interface paths {
     get: operations["route_get_map_map_get"]
   }
   "/map/labelled_map.nii.gz": {
-    /** Route Get Parcellation Labelled Map */
+    /**
+     * Route Get Parcellation Labelled Map 
+     * @description Returns a labelled map if region_id is not provided.
+     * 
+     * Returns a mask if a region_id is provided.
+     * 
+     * region_id MAY refer to ANY region on the region hierarchy, and a combined mask will be returned.
+     */
     get: operations["route_get_parcellation_labelled_map_map_labelled_map_nii_gz_get"]
   }
   "/map/statistical_map.nii.gz": {
-    /** Route Get Region Statistical Map */
+    /**
+     * Route Get Region Statistical Map 
+     * @description Returns a statistic map.
+     * 
+     * region_id MUST refer to leaf region on the region hierarchy.
+     */
     get: operations["route_get_region_statistical_map_map_statistical_map_nii_gz_get"]
   }
   "/map/statistical_map.info.json": {
@@ -81,13 +93,13 @@ export interface paths {
     /** Get Single Tabular */
     get: operations["get_single_tabular_feature_Tabular__feature_id__get"]
   }
-  "/feature/VolumeOfInterest": {
+  "/feature/Image": {
     /** Get All Voi */
-    get: operations["get_all_voi_feature_VolumeOfInterest_get"]
+    get: operations["get_all_voi_feature_Image_get"]
   }
-  "/feature/VolumeOfInterest/{feature_id}": {
+  "/feature/Image/{feature_id}": {
     /** Get Single Voi */
-    get: operations["get_single_voi_feature_VolumeOfInterest__feature_id__get"]
+    get: operations["get_single_voi_feature_Image__feature_id__get"]
   }
   "/feature/GeneExpressions": {
     /** Get All Gene */
@@ -97,6 +109,16 @@ export interface paths {
     /** Get All Gene */
     get: operations["get_all_gene_feature_GeneExpressions__feature_id__get"]
   }
+  "/feature/{feature_id}": {
+    /**
+     * Get Single Feature 
+     * @description This endpoint allows detail of a single feature to be fetched, without the necessary context. However, the tradeoff for this endpoint is:
+     * 
+     * - the endpoint typing is the union of all possible return types
+     * - the client needs to supply any necessary query param (e.g. subject for regional connectivity, gene for gene expression etc)
+     */
+    get: operations["get_single_feature_feature__feature_id__get"]
+  }
 }
 
 export type webhooks = Record<string, never>;
@@ -341,9 +363,9 @@ export interface components {
       axesOrigin: (components["schemas"]["AxesOrigin"])[]
       /**
        * defaultImage 
-       * @description Two or three dimensional image that particluarly represents a specific coordinate space.
+       * @description Two or three dimensional image that particluarly represents a specific coordinate space. Overriden by Siibra API to use as VolumeModel
        */
-      defaultImage?: components["schemas"]["VolumeModel"][]
+      defaultImage?: (components["schemas"]["VolumeModel"])[]
       /**
        * digitalIdentifier 
        * @description Digital handle to identify objects or legal persons.
@@ -505,6 +527,19 @@ export interface components {
       /** Url */
       url: string
     }
+    /** FeatureMetaModel */
+    FeatureMetaModel: {
+      /** Name */
+      name: string
+      /** Path */
+      path?: string
+      /** Query Params */
+      query_params?: (string)[]
+      /** Path Params */
+      path_params?: (string)[]
+      /** Category */
+      category?: string
+    }
     /** HTTPValidationError */
     HTTPValidationError: {
       /** Detail */
@@ -662,8 +697,17 @@ export interface components {
       page: number
       /** Size */
       size: number
-      /** Pages */
-      pages?: number
+    }
+    /** Page[FeatureMetaModel] */
+    Page_FeatureMetaModel_: {
+      /** Items */
+      items: (components["schemas"]["FeatureMetaModel"])[]
+      /** Total */
+      total: number
+      /** Page */
+      page: number
+      /** Size */
+      size: number
     }
     /** Page[ParcellationEntityVersionModel] */
     Page_ParcellationEntityVersionModel_: {
@@ -675,8 +719,6 @@ export interface components {
       page: number
       /** Size */
       size: number
-      /** Pages */
-      pages?: number
     }
     /** Page[SiibraAtlasModel] */
     Page_SiibraAtlasModel_: {
@@ -688,8 +730,6 @@ export interface components {
       page: number
       /** Size */
       size: number
-      /** Pages */
-      pages?: number
     }
     /** Page[SiibraCorticalProfileModel] */
     Page_SiibraCorticalProfileModel_: {
@@ -701,8 +741,6 @@ export interface components {
       page: number
       /** Size */
       size: number
-      /** Pages */
-      pages?: number
     }
     /** Page[SiibraParcellationModel] */
     Page_SiibraParcellationModel_: {
@@ -714,8 +752,6 @@ export interface components {
       page: number
       /** Size */
       size: number
-      /** Pages */
-      pages?: number
     }
     /** Page[SiibraRegionalConnectivityModel] */
     Page_SiibraRegionalConnectivityModel_: {
@@ -727,8 +763,6 @@ export interface components {
       page: number
       /** Size */
       size: number
-      /** Pages */
-      pages?: number
     }
     /** Page[SiibraTabularModel] */
     Page_SiibraTabularModel_: {
@@ -740,8 +774,6 @@ export interface components {
       page: number
       /** Size */
       size: number
-      /** Pages */
-      pages?: number
     }
     /** Page[SiibraVoiModel] */
     Page_SiibraVoiModel_: {
@@ -753,34 +785,17 @@ export interface components {
       page: number
       /** Size */
       size: number
-      /** Pages */
-      pages?: number
-    }
-    /** Page[Union[SiibraCorticalProfileModel, SiibraTabularModel, SiibraReceptorDensityFp]] */
-    Page_Union_SiibraCorticalProfileModel__SiibraTabularModel__SiibraReceptorDensityFp__: {
-      /** Items */
-      items: (components["schemas"]["SiibraCorticalProfileModel"] | components["schemas"]["SiibraTabularModel"] | components["schemas"]["SiibraReceptorDensityFp"])[]
-      /** Total */
-      total: number
-      /** Page */
-      page: number
-      /** Size */
-      size: number
-      /** Pages */
-      pages?: number
     }
-    /** Page[str] */
-    Page_str_: {
+    /** Page[Union[SiibraCorticalProfileModel, SiibraReceptorDensityFp, SiibraTabularModel]] */
+    Page_Union_SiibraCorticalProfileModel__SiibraReceptorDensityFp__SiibraTabularModel__: {
       /** Items */
-      items: (string)[]
+      items: (components["schemas"]["SiibraCorticalProfileModel"] | components["schemas"]["SiibraReceptorDensityFp"] | components["schemas"]["SiibraTabularModel"])[]
       /** Total */
       total: number
       /** Page */
       page: number
       /** Size */
       size: number
-      /** Pages */
-      pages?: number
     }
     /** ParcellationEntityVersionModel */
     ParcellationEntityVersionModel: {
@@ -979,6 +994,8 @@ export interface components {
       id: string
       /** Modality */
       modality: string
+      /** Category */
+      category: string
       /** Description */
       description: string
       /** Name */
@@ -1038,6 +1055,8 @@ export interface components {
       id: string
       /** Modality */
       modality: string
+      /** Category */
+      category: string
       /** Description */
       description: string
       /** Name */
@@ -1067,6 +1086,8 @@ export interface components {
       id: string
       /** Modality */
       modality: string
+      /** Category */
+      category: string
       /** Description */
       description: string
       /** Name */
@@ -1091,6 +1112,8 @@ export interface components {
       id: string
       /** Modality */
       modality: string
+      /** Category */
+      category: string
       /** Description */
       description: string
       /** Name */
@@ -1108,6 +1131,8 @@ export interface components {
       id: string
       /** Modality */
       modality: string
+      /** Category */
+      category: string
       /** Description */
       description: string
       /** Name */
@@ -1453,11 +1478,19 @@ export interface operations {
     }
   }
   route_get_parcellation_labelled_map_map_labelled_map_nii_gz_get: {
-    /** Route Get Parcellation Labelled Map */
+    /**
+     * Route Get Parcellation Labelled Map 
+     * @description Returns a labelled map if region_id is not provided.
+     * 
+     * Returns a mask if a region_id is provided.
+     * 
+     * region_id MAY refer to ANY region on the region hierarchy, and a combined mask will be returned.
+     */
     parameters: {
       query: {
         parcellation_id: string
         space_id: string
+        region_id?: string
       }
     }
     responses: {
@@ -1472,7 +1505,12 @@ export interface operations {
     }
   }
   route_get_region_statistical_map_map_statistical_map_nii_gz_get: {
-    /** Route Get Region Statistical Map */
+    /**
+     * Route Get Region Statistical Map 
+     * @description Returns a statistic map.
+     * 
+     * region_id MUST refer to leaf region on the region hierarchy.
+     */
     parameters: {
       query: {
         parcellation_id: string
@@ -1527,7 +1565,7 @@ export interface operations {
       /** @description Successful Response */
       200: {
         content: {
-          "application/json": components["schemas"]["Page_str_"]
+          "application/json": components["schemas"]["Page_FeatureMetaModel_"]
         }
       }
       /** @description Validation Error */
@@ -1658,7 +1696,7 @@ export interface operations {
       /** @description Successful Response */
       200: {
         content: {
-          "application/json": components["schemas"]["Page_Union_SiibraCorticalProfileModel__SiibraTabularModel__SiibraReceptorDensityFp__"]
+          "application/json": components["schemas"]["Page_Union_SiibraCorticalProfileModel__SiibraReceptorDensityFp__SiibraTabularModel__"]
         }
       }
       /** @description Validation Error */
@@ -1685,7 +1723,7 @@ export interface operations {
       /** @description Successful Response */
       200: {
         content: {
-          "application/json": components["schemas"]["SiibraCorticalProfileModel"] | components["schemas"]["SiibraTabularModel"] | components["schemas"]["SiibraReceptorDensityFp"]
+          "application/json": components["schemas"]["SiibraCorticalProfileModel"] | components["schemas"]["SiibraReceptorDensityFp"] | components["schemas"]["SiibraTabularModel"]
         }
       }
       /** @description Validation Error */
@@ -1696,7 +1734,7 @@ export interface operations {
       }
     }
   }
-  get_all_voi_feature_VolumeOfInterest_get: {
+  get_all_voi_feature_Image_get: {
     /** Get All Voi */
     parameters: {
       query: {
@@ -1721,7 +1759,7 @@ export interface operations {
       }
     }
   }
-  get_single_voi_feature_VolumeOfInterest__feature_id__get: {
+  get_single_voi_feature_Image__feature_id__get: {
     /** Get Single Voi */
     parameters: {
       query: {
@@ -1800,4 +1838,32 @@ export interface operations {
       }
     }
   }
+  get_single_feature_feature__feature_id__get: {
+    /**
+     * Get Single Feature 
+     * @description This endpoint allows detail of a single feature to be fetched, without the necessary context. However, the tradeoff for this endpoint is:
+     * 
+     * - the endpoint typing is the union of all possible return types
+     * - the client needs to supply any necessary query param (e.g. subject for regional connectivity, gene for gene expression etc)
+     */
+    parameters: {
+      path: {
+        feature_id: string
+      }
+    }
+    responses: {
+      /** @description Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["SiibraVoiModel"] | components["schemas"]["SiibraCorticalProfileModel"] | components["schemas"]["SiibraRegionalConnectivityModel"] | components["schemas"]["SiibraReceptorDensityFp"] | components["schemas"]["SiibraTabularModel"]
+        }
+      }
+      /** @description Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
 }
diff --git a/src/atlasComponents/sapi/sxplrTypes.ts b/src/atlasComponents/sapi/sxplrTypes.ts
index 7626acbf313b9e0889dbbf4013aa0fb295875172..28622c040ca66f8161688f3f9c184a138d4812e4 100644
--- a/src/atlasComponents/sapi/sxplrTypes.ts
+++ b/src/atlasComponents/sapi/sxplrTypes.ts
@@ -77,6 +77,17 @@ export { TThreeSurferMesh, TThreeMesh, TThreeMeshLabel }
  */
 export type TemplateDefaultImage = NgLayerSpec | TThreeMesh
 
+export type LabelledMap = {
+  name: string
+  label: number
+}
+
+export type StatisticalMap = {
+  url: string
+  min: number
+  max: number
+}
+
 /**
  * Features
  */
@@ -84,6 +95,7 @@ export type TemplateDefaultImage = NgLayerSpec | TThreeMesh
 export type Feature = {
   id: string
   name: string
+  category?: string
 } & Partial<AdditionalInfo>
 
 type DataFrame = {
@@ -94,25 +106,21 @@ export type VoiFeature = {
   bbox: BoundingBox
 } & Feature
 
+type CorticalDataType = number
+
+export type CorticalFeature<T extends CorticalDataType, IndexType extends string|number=string> = {
+  indices?: IndexType[]
+  corticalProfile?: T[]
+} & Feature
+
 type TabularDataType = number | string | number[]
 
 export type TabularFeature<T extends TabularDataType> = {
   index: string[]
   columns: string[]
-  data: T[][]
+  data?: T[][]
 } & Feature
 
-export type LabelledMap = {
-  name: string
-  label: number
-}
-
-export type StatisticalMap = {
-  url: string
-  min: number
-  max: number
-}
-
 
 /**
  * Support types
diff --git a/src/atlasComponents/sapi/translateV3.ts b/src/atlasComponents/sapi/translateV3.ts
index d8505ceda11b8c093295f6e3cec2608014f35026..4134af67f94b6ca59e8d72bbf4ab94ac76d527cd 100644
--- a/src/atlasComponents/sapi/translateV3.ts
+++ b/src/atlasComponents/sapi/translateV3.ts
@@ -1,5 +1,5 @@
 import {
-  SxplrAtlas, SxplrParcellation, SxplrTemplate, SxplrRegion, NgLayerSpec, NgPrecompMeshSpec, NgSegLayerSpec, VoiFeature, Point, TemplateDefaultImage, TThreeSurferMesh, TThreeMesh, LabelledMap
+  SxplrAtlas, SxplrParcellation, SxplrTemplate, SxplrRegion, NgLayerSpec, NgPrecompMeshSpec, NgSegLayerSpec, VoiFeature, Point, TemplateDefaultImage, TThreeSurferMesh, TThreeMesh, LabelledMap, CorticalFeature
 } from "./sxplrTypes"
 import { PathReturn } from "./typeV3"
 import { hexToRgb } from 'common/util'
@@ -63,7 +63,18 @@ class TranslateV3 {
       name: region.name,
       color: hexToRgb(region.hasAnnotation?.displayColor) as [number, number, number],
       parentIds: region.hasParent.map( v => v["@id"] ),
-      type: "SxplrRegion"
+      type: "SxplrRegion",
+      centroid: region.hasAnnotation?.bestViewPoint
+        ? await (async () => {
+          const bestViewPoint = region.hasAnnotation?.bestViewPoint
+          const fullSpace = this.#templateMap.get(bestViewPoint.coordinateSpace['@id'])
+          const space = await this.translateTemplate(fullSpace)
+          return {
+            loc: bestViewPoint.coordinates.map(v => v.value) as [number, number, number],
+            space
+          }
+        })()
+        : null
     }
   }
 
@@ -322,7 +333,7 @@ class TranslateV3 {
     }
   }
 
-  async translateVoi(voi: PathReturn<"/feature/VolumeOfInterest/{feature_id}">): Promise<VoiFeature> {
+  async translateVoi(voi: PathReturn<"/feature/Image/{feature_id}">): Promise<VoiFeature> {
     const { boundingbox } = voi
     const { loc: center, space } = await this.translatePoint(boundingbox.center)
     const { loc: maxpoint } = await this.translatePoint(boundingbox.maxpoint)
@@ -339,6 +350,29 @@ class TranslateV3 {
       id: voi.id
     }
   }
+
+  
+  async translateCorticalProfile(feat: PathReturn<"/feature/CorticalProfile/{feature_id}">): Promise<CorticalFeature<number>> {
+    return {
+      id: feat.id,
+      name: feat.name,
+      desc: feat.description,
+      link: [
+        ...feat.datasets
+          .map(ds => ds.urls)
+          .flatMap(v => v)
+          .map(url => ({
+            href: url.url,
+            text: url.url
+          })),
+        ...feat.datasets
+          .map(ds => ({
+            href: ds.ebrains_page,
+            text: "ebrains resource"
+          }))
+      ]
+    }
+  }
 }
 
 export const translateV3Entities = new TranslateV3()
diff --git a/src/atlasComponents/sapi/typeV3.ts b/src/atlasComponents/sapi/typeV3.ts
index 7aa938fdae4f5496cfdaf06e4e535df633f62f3b..d65db66debe2bff385851d785213cec40749b3c5 100644
--- a/src/atlasComponents/sapi/typeV3.ts
+++ b/src/atlasComponents/sapi/typeV3.ts
@@ -12,11 +12,23 @@ export type SxplrCoordinatePointExtension = {
   color: string
   '@id': string // should match the id of opendminds specs
 }
-export type SapiSpatialFeatureModel = PathReturn<"/feature/VolumeOfInterest/{feature_id}">
+export type SapiSpatialFeatureModel = PathReturn<"/feature/Image/{feature_id}">
 export type SapiFeatureModel = SapiSpatialFeatureModel | PathReturn<"/feature/Tabular/{feature_id}"> | PathReturn<"/feature/RegionalConnectivity/{feature_id}"> | PathReturn<"/feature/CorticalProfile/{feature_id}">
 
 export type SapiRoute = keyof paths
 
+type _FeatureType<FeatureRoute extends SapiRoute> = FeatureRoute extends `/feature/${infer FT}`
+  ? FT extends "_types"
+    ? never
+    : FT extends "{feature_id}"
+      ? never
+      : FT extends `${infer _FT}/{${infer _FID}}`
+        ? never
+        : FT
+  : never
+
+export type FeatureType = _FeatureType<SapiRoute>
+
 /**
  * Support types
  */
diff --git a/src/atlasComponents/sapiViews/core/module.ts b/src/atlasComponents/sapiViews/core/module.ts
index c30a1d83ebe1a72a911acbfdc796729e683431d8..d307b3b54b77681c32a3c8cc8cbbbd0a3cb2cf14 100644
--- a/src/atlasComponents/sapiViews/core/module.ts
+++ b/src/atlasComponents/sapiViews/core/module.ts
@@ -1,7 +1,6 @@
 import { CommonModule } from "@angular/common";
 import { NgModule } from "@angular/core";
 import { SapiViewsCoreAtlasModule } from "./atlas/module";
-import { SapiViewsCoreDatasetModule } from "./datasets";
 import { SapiViewsCoreParcellationModule } from "./parcellation/module";
 import { SapiViewsCoreRegionModule } from "./region";
 import { SapiViewsCoreRichModule } from "./rich/module";
@@ -10,7 +9,6 @@ import { SapiViewsCoreSpaceModule } from "./space";
 @NgModule({
   imports: [
     CommonModule,
-    SapiViewsCoreDatasetModule,
     SapiViewsCoreRegionModule,
     SapiViewsCoreAtlasModule,
     SapiViewsCoreSpaceModule,
@@ -18,7 +16,6 @@ import { SapiViewsCoreSpaceModule } from "./space";
     SapiViewsCoreRichModule,
   ],
   exports: [
-    SapiViewsCoreDatasetModule,
     SapiViewsCoreRegionModule,
     SapiViewsCoreAtlasModule,
     SapiViewsCoreSpaceModule,
diff --git a/src/atlasComponents/sapiViews/core/parcellation/parcellationGroupSelected.pipe.ts b/src/atlasComponents/sapiViews/core/parcellation/parcellationGroupSelected.pipe.ts
index 31b8acbe287630ef23e7006e2bbafc3439a9d666..97867311c4d2af1aa72992d683a35a744afab168 100644
--- a/src/atlasComponents/sapiViews/core/parcellation/parcellationGroupSelected.pipe.ts
+++ b/src/atlasComponents/sapiViews/core/parcellation/parcellationGroupSelected.pipe.ts
@@ -14,6 +14,6 @@ function isGroupedParc(parc: GroupedParcellation|unknown): parc is GroupedParcel
 export class ParcellationGroupSelectedPipe implements PipeTransform {
   public transform(parc: GroupedParcellation|unknown, selectedParcellation: SxplrParcellation): boolean {
     if (!isGroupedParc(parc)) return false
-    return parc.parcellations.some(p => p["@id"] === selectedParcellation["@id"])
+    return parc.parcellations.some(p => p.id === selectedParcellation.id)
   }
 }
diff --git a/src/atlasComponents/sapiViews/core/region/module.ts b/src/atlasComponents/sapiViews/core/region/module.ts
index 50759b93a63e6993946a337212559589c38e31a6..faee32dfa4dcb15ff6f70e44ddde84d6a36a93b5 100644
--- a/src/atlasComponents/sapiViews/core/region/module.ts
+++ b/src/atlasComponents/sapiViews/core/region/module.ts
@@ -2,6 +2,7 @@ import { CommonModule } from "@angular/common";
 import { NgModule } from "@angular/core";
 import { MarkdownModule } from "src/components/markdown";
 import { SpinnerModule } from "src/components/spinner";
+import { FeatureModule } from "src/features";
 import { AngularMaterialModule } from "src/sharedModules";
 import { StrictLocalModule } from "src/strictLocal";
 // import { SapiViewsFeaturesModule } from "../../features";
@@ -21,6 +22,7 @@ import { SapiViewsCoreRegionRegionRich } from "./region/rich/region.rich.compone
     SpinnerModule,
     MarkdownModule,
     StrictLocalModule,
+    FeatureModule,
   ],
   declarations: [
     SapiViewsCoreRegionRegionListItem,
diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html
index 461fcf176f48d78582a92a6f7e2149ee0b500450..c3acb2dfb3de57075448fc2e25615408f24bf6f5 100644
--- a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html
+++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html
@@ -82,6 +82,11 @@
     </markdown-dom>
   </ng-template>
 
+  <sxplr-feature-entry
+    [parcellation]="parcellation"
+    [region]="region">
+  </sxplr-feature-entry>
+
   <mat-accordion class="d-block mt-2">
 
     <!-- desc -->
@@ -100,7 +105,7 @@
     <ng-template [ngIf]="!environment.STRICT_LOCAL">
 
       <!-- feature list -->
-      <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
+      <!-- <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
         title: CONST.REGIONAL_FEATURES,
         iconClass: 'fas fa-database',
         content: kgRegionalFeatureList,
@@ -108,7 +113,7 @@
         iconTooltip: 'Regional Features',
         iavNgIf: true
       }">
-      </ng-container>
+      </ng-container> -->
 
       <!-- connectivity -->
       <ng-template #sxplrSapiviewsFeaturesConnectivityBrowser>
diff --git a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html
index 044965ed56ec6ad515764fb75a503bcea7269220..58378b95d7c01864f9b6eeef3dcae8e8ce707848 100644
--- a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html
+++ b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html
@@ -99,7 +99,7 @@
     <ng-template [ngIf]="selectedIds" let-selectedIds>
       <mat-icon
         fontSet="fas"
-        [fontIcon]="selectedIds.includes(item['@id']) ? 'fa-circle' : 'fa-none'"
+        [fontIcon]="selectedIds.includes(item.id) ? 'fa-circle' : 'fa-none'"
         >
       </mat-icon>
     </ng-template>
diff --git a/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.html b/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..203d751df7c6adf7f028f84016545e126450117c
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.html
@@ -0,0 +1,68 @@
+<ng-template #headerTmpl>
+  <ng-content select="[header]"></ng-content>
+</ng-template>
+
+<mat-card *ngIf="!feature">
+
+  <ng-template [ngTemplateOutlet]="headerTmpl"></ng-template>
+  <span>
+    Feature not specified.
+  </span>
+</mat-card>
+
+<mat-card *ngIf="feature"
+  class="mat-elevation-z4 sxplr-z-4">
+  <mat-card-title>
+    <ng-template [ngTemplateOutlet]="headerTmpl"></ng-template>
+    <div class="feature-title">
+      {{ feature.name }}
+    </div>
+  </mat-card-title>
+
+  <mat-card-subtitle class="sxplr-d-inline-flex sxplr-align-items-stretch">
+    <ng-template [ngIf]="feature.category">
+      <mat-icon class="sxplr-m-a" fontSet="fas" fontIcon="fa-database"></mat-icon>
+      <span class="sxplr-m-a">
+        {{ feature.category }}
+      </span>
+    </ng-template>
+
+    <mat-divider class="sxplr-pl-1" [vertical]="true"></mat-divider>
+
+    <ng-template [ngIf]="busy$ | async">
+      <spinner-cmp></spinner-cmp>
+    </ng-template>
+
+    <!-- <a mat-icon-button sxplr-hide-when-local *ngFor="let url of dataset.link" [href]="url.doi | parseDoi" target="_blank">
+      <i class="fas fa-external-link-alt"></i>
+    </a> -->
+  </mat-card-subtitle>
+</mat-card>
+
+<mat-card *ngIf="feature" class="sxplr-z-0">
+  <mat-card-content>
+    <!-- TODO fix feature typing! with proper translate fn -->
+    <markdown-dom class="sxplr-muted" [markdown]="feature['description']">
+    </markdown-dom>
+  </mat-card-content>
+</mat-card>
+
+
+<!-- tabular special view -->
+<ng-template [ngIf]="tabular$ | async" let-tabular>
+  <table class="feature-detail" mat-table [dataSource]="tabular | transformPdToDs">
+
+    <ng-container *ngFor="let column of columns$ | async"
+      [matColumnDef]="column">
+      <th mat-header-cell *matHeaderCellDef>
+        {{ column }}
+      </th>
+      <td mat-cell *matCellDef="let element">
+        {{ element[column] }}
+      </td>
+    </ng-container>
+
+    <tr mat-header-row *matHeaderRowDef="columns$ | async"></tr>
+    <tr mat-row *matRowDef="let row; columns: columns$ | async;"></tr>
+  </table>
+</ng-template>
diff --git a/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.scss b/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e39c0ecb5869b91204fd4782600e6686f8581cee
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.scss
@@ -0,0 +1,11 @@
+.feature-title
+{
+    max-height: 8rem;
+    overflow-x: hidden;
+    overflow-y: auto;
+}
+
+.feature-detail
+{
+    width: 100%;
+}
diff --git a/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.spec.ts b/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6606d5571625b9e2f89713a26d37626d95b1ac1b
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FeatureViewComponent } from './feature-view.component';
+
+describe('FeatureViewComponent', () => {
+  let component: FeatureViewComponent;
+  let fixture: ComponentFixture<FeatureViewComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ FeatureViewComponent ]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(FeatureViewComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.ts b/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..903ec3ffef6a55045266ce0efea3c610fd5d1630
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.ts
@@ -0,0 +1,50 @@
+import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit } from '@angular/core';
+import { BehaviorSubject, Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { SAPI } from 'src/atlasComponents/sapi/sapi.service';
+import { Feature, TabularFeature } from 'src/atlasComponents/sapi/sxplrTypes';
+
+function isTabularData(feature: unknown): feature is { data: TabularFeature<number|string|number[]> } {
+  return feature['@type'].includes("siibra-0.4/feature/tabular")
+}
+
+@Component({
+  selector: 'sxplr-feature-view',
+  templateUrl: './feature-view.component.html',
+  styleUrls: ['./feature-view.component.scss'],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FeatureViewComponent implements OnChanges {
+
+  @Input()
+  feature: Feature
+
+  busy$ = new BehaviorSubject<boolean>(false)
+
+  tabular$: BehaviorSubject<TabularFeature<number|string|number[]>> = new BehaviorSubject(null)
+  columns$: Observable<string[]> = this.tabular$.pipe(
+    map(data => data
+      ? ['index', ...data.columns]
+      : []),
+  )
+  constructor(private sapi: SAPI) { }
+
+  ngOnChanges(): void {
+    console.log(this.feature)
+    this.tabular$.next(null)
+    this.busy$.next(true)
+    this.sapi.v3Get("/feature/{feature_id}", {
+      path: {
+        feature_id: this.feature.id
+      }
+    }).subscribe(
+      val => {
+        this.busy$.next(false)
+
+        if (!isTabularData(val)) return
+        this.tabular$.next(val.data)
+      },
+      () => this.busy$.next(false)
+    )
+  }
+}
diff --git a/src/atlasComponents/sapiViews/features/features.module.ts b/src/atlasComponents/sapiViews/features/features.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c05518863875722d5a7f11dcf77938a2188c1866
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/features.module.ts
@@ -0,0 +1,31 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FeatureViewComponent } from './feature-view/feature-view.component';
+import { MatCardModule } from '@angular/material/card';
+import { MatIconModule } from '@angular/material/icon';
+import { MatDividerModule } from '@angular/material/divider';
+import { MatTableModule } from "@angular/material/table"
+import { MarkdownModule } from 'src/components/markdown';
+import { TransformPdToDsPipe } from './transform-pd-to-ds.pipe';
+import { SpinnerModule } from 'src/components/spinner';
+
+
+@NgModule({
+  declarations: [
+    FeatureViewComponent,
+    TransformPdToDsPipe,
+  ],
+  imports: [
+    CommonModule,
+    MatCardModule,
+    MatIconModule,
+    MatDividerModule,
+    MarkdownModule,
+    MatTableModule,
+    SpinnerModule,
+  ],
+  exports: [
+    FeatureViewComponent,
+  ]
+})
+export class FeaturesModule { }
diff --git a/src/atlasComponents/sapiViews/features/transform-pd-to-ds.pipe.spec.ts b/src/atlasComponents/sapiViews/features/transform-pd-to-ds.pipe.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..66eb0d5f6e111a63b285e8d1dcf3460759468bcc
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/transform-pd-to-ds.pipe.spec.ts
@@ -0,0 +1,8 @@
+import { TransformPdToDsPipe } from './transform-pd-to-ds.pipe';
+
+describe('TransformPdToDsPipe', () => {
+  it('create an instance', () => {
+    const pipe = new TransformPdToDsPipe();
+    expect(pipe).toBeTruthy();
+  });
+});
diff --git a/src/atlasComponents/sapiViews/features/transform-pd-to-ds.pipe.ts b/src/atlasComponents/sapiViews/features/transform-pd-to-ds.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..09f598d9d6ceea05a41c7599d5ea90a45b32b5b0
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/transform-pd-to-ds.pipe.ts
@@ -0,0 +1,23 @@
+import { CdkTableDataSourceInput } from '@angular/cdk/table';
+import { Pipe, PipeTransform } from '@angular/core';
+import { TabularFeature } from 'src/atlasComponents/sapi/sxplrTypes';
+
+@Pipe({
+  name: 'transformPdToDs',
+  pure: true
+})
+export class TransformPdToDsPipe implements PipeTransform {
+
+  transform(pd: TabularFeature<string|number|number[]>): CdkTableDataSourceInput<unknown> {
+    return pd.data.map((arr, idx) => {
+      const returnVal: Record<string, string|number|number[]> = {
+        index: pd.index[idx],
+      }
+      arr.forEach((val, colIdx) => {
+        returnVal[pd.columns[colIdx]] = val
+      })
+      return returnVal
+    })
+  }
+
+}
diff --git a/src/atlasComponents/sapiViews/module.ts b/src/atlasComponents/sapiViews/module.ts
index dd53fa9756db3e026c90a5c95c6b2ec228cec616..7e62c1e29a0be3110390d263fb2bc6e0c2b7d37d 100644
--- a/src/atlasComponents/sapiViews/module.ts
+++ b/src/atlasComponents/sapiViews/module.ts
@@ -1,13 +1,14 @@
 import { NgModule } from "@angular/core";
 import { SapiViewsCoreModule } from "./core";
+import { FeaturesModule } from "./features/features.module";
 
 @NgModule({
   imports: [
-    // SapiViewsFeaturesModule,
+    FeaturesModule,
     SapiViewsCoreModule,
   ],
   exports: [
-    // SapiViewsFeaturesModule,
+    FeaturesModule,
     SapiViewsCoreModule,
   ]
 })
diff --git a/src/atlasComponents/sapiViews/richDataset.stories.ts b/src/atlasComponents/sapiViews/richDataset.stories.ts
index 03ac405b682240aedd06eac97db2b8d659e55d00..112fcebe070eb9d518e7612e0f4a2bfb11ad85f1 100644
--- a/src/atlasComponents/sapiViews/richDataset.stories.ts
+++ b/src/atlasComponents/sapiViews/richDataset.stories.ts
@@ -7,7 +7,6 @@ import { SAPI } from "src/atlasComponents/sapi"
 import { getHoc1RightFeatureDetail, getHoc1RightFeatures, getHoc1Right, getHumanAtlas, getJba29, getMni152, provideDarkTheme, getMni152SpatialFeatureHoc1Right } from "src/atlasComponents/sapi/stories.base"
 import { AngularMaterialModule } from "src/sharedModules"
 import { cleanIeegSessionDatasets, SapiAtlasModel, SapiFeatureModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "../sapi/type"
-import { SapiViewsCoreDatasetModule } from "./core/datasets"
 import { SapiViewsFeaturesModule } from "./features"
 
 @Component({
@@ -52,7 +51,6 @@ export default {
         AngularMaterialModule,
         CommonModule,
         HttpClientModule,
-        SapiViewsCoreDatasetModule,
         SapiViewsFeaturesModule,
       ],
       providers: [
diff --git a/src/components/smartChip/component/smartChip.component.ts b/src/components/smartChip/component/smartChip.component.ts
index d933960efe0b817d2cb3c29b23b306c62706df04..2e2ee0b7a9ae97931ba9d0a65dd84f302051fc3a 100644
--- a/src/components/smartChip/component/smartChip.component.ts
+++ b/src/components/smartChip/component/smartChip.component.ts
@@ -36,7 +36,7 @@ const cssColorToHsl = (input: string) => {
   selector: `sxplr-smart-chip`,
   templateUrl: `./smartChip.template.html`,
   styleUrls: [
-    `/smartChip.style.css`
+    `./smartChip.style.scss`
   ],
   changeDetection: ChangeDetectionStrategy.OnPush
 })
diff --git a/src/components/smartChip/component/smartChip.style.css b/src/components/smartChip/component/smartChip.style.scss
similarity index 100%
rename from src/components/smartChip/component/smartChip.style.css
rename to src/components/smartChip/component/smartChip.style.scss
diff --git a/src/features/base.ts b/src/features/base.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cda6bb56ed795abbfaecf68dcdf23e9644c2297d
--- /dev/null
+++ b/src/features/base.ts
@@ -0,0 +1,37 @@
+import { Input, OnChanges, Directive } from "@angular/core";
+import { BehaviorSubject } from "rxjs";
+import { SxplrParcellation, SxplrRegion, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
+
+@Directive()
+export class FeatureBase implements OnChanges{
+    
+  @Input()
+  template: SxplrTemplate
+
+  @Input()
+  parcellation: SxplrParcellation
+
+  @Input()
+  region: SxplrRegion
+
+  @Input()
+  queryParams: Record<string, string> = {}
+  
+  protected TPR$ = new BehaviorSubject<{ template?: SxplrTemplate, parcellation?: SxplrParcellation, region?: SxplrRegion }>({ template: null, parcellation: null, region: null })
+
+  ngOnChanges(): void {
+    const { template, parcellation, region } = this
+    this.TPR$.next({ template, parcellation, region })
+  }
+}
+
+
+
+export const AllFeatures = {
+  CorticalProfile: "CorticalProfile",
+  // EbrainsDataFeature: "EbrainsDataFeature",
+  RegionalConnectivity: "RegionalConnectivity",
+  Tabular: "Tabular",
+  // GeneExpressions: "GeneExpressions",
+  Image: "Image",
+} as const
\ No newline at end of file
diff --git a/src/features/category-acc.directive.spec.ts b/src/features/category-acc.directive.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2cdbb5846fc5df06a4f5ba60672f803700c78341
--- /dev/null
+++ b/src/features/category-acc.directive.spec.ts
@@ -0,0 +1,8 @@
+import { CategoryAccDirective } from './category-acc.directive';
+
+describe('CategoryAccDirective', () => {
+  it('should create an instance', () => {
+    const directive = new CategoryAccDirective();
+    expect(directive).toBeTruthy();
+  });
+});
diff --git a/src/features/category-acc.directive.ts b/src/features/category-acc.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b998efb0a8cd27b8260424ee8928bf7a3f2efbd9
--- /dev/null
+++ b/src/features/category-acc.directive.ts
@@ -0,0 +1,54 @@
+import { AfterContentInit, ContentChildren, Directive, OnDestroy, QueryList } from '@angular/core';
+import { BehaviorSubject, combineLatest, Subscription } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { ListComponent } from './list/list.component';
+
+@Directive({
+  selector: '[sxplrCategoryAcc]',
+  exportAs: 'categoryAcc'
+})
+export class CategoryAccDirective implements AfterContentInit, OnDestroy {
+
+  constructor() { }
+
+  public isBusy$ = new BehaviorSubject<boolean>(false)
+  public total$ = new BehaviorSubject<number>(0)
+
+  @ContentChildren(ListComponent, { read: ListComponent, descendants: true })
+  listCmps: QueryList<ListComponent>
+
+  #changeSub: Subscription
+  ngAfterContentInit(): void {
+    this.#registerListCmps()
+    this.#changeSub = this.listCmps.changes.subscribe(() => this.#registerListCmps())
+  }
+
+  ngOnDestroy(): void {
+    this.#cleanup()
+  }
+
+  #subscriptions: Subscription[] = []
+  #cleanup(){
+    if (this.#changeSub) this.#changeSub.unsubscribe()
+    while(this.#subscriptions.length > 0) this.#subscriptions.pop().unsubscribe()
+  }
+  #registerListCmps(){
+    this.#cleanup()
+
+    const listCmp = Array.from(this.listCmps)
+
+    this.#subscriptions.push(  
+      combineLatest(
+        listCmp.map(listCmp => listCmp.features$)
+      ).pipe(
+        map(features => features.reduce((acc, curr) => acc + curr.length, 0))
+      ).subscribe(total => this.total$.next(total)),
+
+      combineLatest(
+        listCmp.map(listCmp => listCmp.state$)
+      ).pipe(
+        map(states => states.some(state => state === "busy"))
+      ).subscribe(flag => this.isBusy$.next(flag))
+    )
+  }
+}
diff --git a/src/features/entry/entry.component.html b/src/features/entry/entry.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..bd413fc42578e9e81770dcfb62a677fcd70da7f8
--- /dev/null
+++ b/src/features/entry/entry.component.html
@@ -0,0 +1,65 @@
+<mat-card>
+    <mat-accordion>
+        <mat-expansion-panel *ngFor="let keyvalue of (cateogryCollections$ | async | keyvalue)"
+            sxplrCategoryAcc
+            #categoryAcc="categoryAcc"
+            [ngClass]="{
+                'sxplr-d-none': !(categoryAcc.isBusy$ | async) && (categoryAcc.total$ | async) === 0
+            }">
+
+            <mat-expansion-panel-header>
+
+                <mat-panel-title>
+                    {{ keyvalue.key }}
+                </mat-panel-title>
+                
+                <mat-panel-description>
+                    <spinner-cmp *ngIf="categoryAcc.isBusy$ | async"></spinner-cmp>
+                    <span>
+                        {{ categoryAcc.total$ | async }}
+                    </span>
+                </mat-panel-description>
+            </mat-expansion-panel-header>
+
+            <div class="c3-outer">
+                <div class="c3-inner">
+                    
+                    <mat-card class="c3 mat-elevation-z4"
+                        *ngFor="let feature of keyvalue.value"
+                        [ngClass]="{
+                            'sxplr-d-none': (list.state$ | async) === 'noresult'
+                        }">
+                        <mat-card-header>
+
+                            <mat-card-title>
+                                <span class="category-title sxplr-white-space-nowrap">
+                                    {{ feature.name | featureNamePipe }}
+                                </span>
+                            </mat-card-title>
+                            
+                        </mat-card-header>
+
+
+                        <mat-card-content>
+                            <sxplr-feature-list
+                                [template]="template"
+                                [parcellation]="parcellation"
+                                [region]="region"
+                                [queryParams]="queryParams | mergeObj : { type: (feature.name | featureNamePipe) }"
+                                [featureRoute]="feature.path"
+                                (onClickFeature)="onClickFeature($event)"
+                                #list="featureList"
+                                >
+                            </sxplr-feature-list>
+
+                            <spinner-cmp *ngIf="(list.state$ | async) === 'busy'"></spinner-cmp>
+                            
+                        </mat-card-content>
+                    </mat-card>
+                </div>
+            </div>
+        </mat-expansion-panel>
+    </mat-accordion>
+
+</mat-card>
+
diff --git a/src/features/entry/entry.component.scss b/src/features/entry/entry.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..44bcd0aaaf08d7a4741746162402595f23e08b63
--- /dev/null
+++ b/src/features/entry/entry.component.scss
@@ -0,0 +1,62 @@
+:host > mat-card
+{
+  padding-left: 0;
+  padding-right: 0;
+}
+
+mat-list-item
+{
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+// card in card container
+.c3-outer
+{
+  display: inline-block;
+  overflow-x: auto;
+  height: 15rem;
+  width: 100%;
+}
+
+.c3-inner
+{
+
+  height: 100%;
+  display: inline-flex;
+  gap: 1.5rem;
+  flex-wrap: nowrap;
+  
+  margin: 0 2rem;
+  align-items: stretch;
+}
+
+.c3-inner > mat-card
+{
+  width: 16rem;
+  overflow:hidden;
+  height: 95%;
+}
+
+.category-title:hover
+{
+  cursor: default;
+}
+
+mat-card.c3
+{
+  display: flex;
+  flex-direction: column;
+}
+
+mat-card.c3 > mat-card-header
+{
+  flex: 0 0 auto;
+}
+
+mat-card.c3 > mat-card-content
+{
+  flex: 1 1 auto;
+  height: 75%;
+  overflow: auto;
+}
\ No newline at end of file
diff --git a/src/features/entry/entry.component.spec.ts b/src/features/entry/entry.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..198c166cd85f9ce339f243f948689b83c6119649
--- /dev/null
+++ b/src/features/entry/entry.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { EntryComponent } from './entry.component';
+
+describe('EntryComponent', () => {
+  let component: EntryComponent;
+  let fixture: ComponentFixture<EntryComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ EntryComponent ]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(EntryComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/features/entry/entry.component.ts b/src/features/entry/entry.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e2bb436d012072e10c44060a60f4d8f4c2bf7096
--- /dev/null
+++ b/src/features/entry/entry.component.ts
@@ -0,0 +1,73 @@
+import { Component } from '@angular/core';
+import { Store } from '@ngrx/store';
+import { map, switchMap, tap } from 'rxjs/operators';
+import { SAPI } from 'src/atlasComponents/sapi';
+import { Feature } from 'src/atlasComponents/sapi/sxplrTypes';
+import { FeatureBase } from '../base';
+import * as userInteraction from "src/state/userInteraction"
+
+const categoryAcc = <T extends Record<string, unknown>>(categories: T[]) => {
+  const returnVal: Record<string, T[]> = {}
+  for (const item of categories) {
+    const { category, ...rest } = item
+    if (!category) continue
+    if (typeof category !== "string") continue
+    if (!returnVal[category]) {
+      returnVal[category] = []
+    }
+    returnVal[category].push(item)
+  }
+  return returnVal
+}
+
+@Component({
+  selector: 'sxplr-feature-entry',
+  templateUrl: './entry.component.html',
+  styleUrls: ['./entry.component.scss']
+})
+export class EntryComponent extends FeatureBase {
+
+  constructor(private sapi: SAPI, private store: Store) {
+    super()
+  }
+
+  private featureTypes$ = this.sapi.v3Get("/feature/_types", {}).pipe(
+    switchMap(resp => 
+      this.sapi.iteratePages(
+        resp,
+        page => this.sapi.v3Get(
+          "/feature/_types",
+          { query: { page } }
+        )
+      )
+    ),
+  )
+
+  public cateogryCollections$ = this.TPR$.pipe(
+    switchMap(({ template, parcellation, region }) => this.featureTypes$.pipe(
+      map(features => {
+        const filteredFeatures = features.filter(v => {
+          const params = [
+            ...(v.path_params || []),
+            ...(v.query_params || []),
+          ]
+          return [
+            params.includes("space_id") === (!!template),
+            params.includes("parcellation_id") === (!!parcellation),
+            params.includes("region_id") === (!!region),
+          ].some(val => val)
+        })
+        return categoryAcc(filteredFeatures)
+      }),
+    )),
+  )
+
+  onClickFeature(feature: Feature) {
+    this.store.dispatch(
+      userInteraction.actions.showFeature({
+        feature
+      })
+    )
+  }
+}
+
diff --git a/src/features/featureName.pipe.ts b/src/features/featureName.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..004eedba3f94b147a77bd2e858d4f7962dce3998
--- /dev/null
+++ b/src/features/featureName.pipe.ts
@@ -0,0 +1,12 @@
+import { Pipe, PipeTransform } from "@angular/core";
+
+@Pipe({
+  name: 'featureNamePipe',
+  pure: true,
+})
+
+export class FeatureNamePipe implements PipeTransform{
+  public transform(name: string): string {
+    return name.split(".").slice(-1)[0]
+  }
+}
diff --git a/src/features/fetch.directive.spec.ts b/src/features/fetch.directive.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5765d9f6e81b81b3b2109a0cc480e13da82ac94a
--- /dev/null
+++ b/src/features/fetch.directive.spec.ts
@@ -0,0 +1,8 @@
+import { FetchDirective } from './fetch.directive';
+
+describe('FetchDirective', () => {
+  it('should create an instance', () => {
+    const directive = new FetchDirective();
+    expect(directive).toBeTruthy();
+  });
+});
diff --git a/src/features/fetch.directive.ts b/src/features/fetch.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eb40e4fd0b1c5bd5c8f33156272eab8598f7c3a2
--- /dev/null
+++ b/src/features/fetch.directive.ts
@@ -0,0 +1,71 @@
+import { Directive, Input, OnChanges, Output, SimpleChanges, EventEmitter } from '@angular/core';
+import { BehaviorSubject, Observable, Subject } from 'rxjs';
+import { switchMap } from 'rxjs/operators';
+import { SAPI } from 'src/atlasComponents/sapi';
+import { SxplrParcellation, SxplrRegion, SxplrTemplate } from 'src/atlasComponents/sapi/sxplrTypes';
+import { FeatureType, SapiRoute } from 'src/atlasComponents/sapi/typeV3';
+import { Feature, CorticalFeature, VoiFeature, TabularFeature } from "src/atlasComponents/sapi/sxplrTypes"
+
+type ObservableOf<Obs extends Observable<unknown>> = Parameters<Obs['subscribe']>[0] extends Function
+? Parameters<Parameters<Obs['subscribe']>[0]>[0]
+: never
+
+const b = new Subject<{ t: string }>()
+
+type typeOfB = ObservableOf<typeof b>
+
+type FeatureMap = {
+  "RegionalConnectivity": Feature
+  "CorticalProfile": CorticalFeature<number>
+  "Tabular": TabularFeature<number|string|number[]>
+  "Image": VoiFeature
+}
+
+@Directive({
+  selector: '[sxplrFeatureFetch]',
+  exportAs: "sxplrFeatureFetch"
+})
+export class FetchDirective<T extends keyof FeatureMap> implements OnChanges {
+
+  /**
+   * TODO check if the decorated property survive on inheritence 
+   */
+
+  @Input()
+  template: SxplrTemplate
+
+  @Input()
+  parcellation: SxplrParcellation
+
+  @Input()
+  region: SxplrRegion
+
+  @Input()
+  featureType: T
+
+  @Output()
+  features: BehaviorSubject<FeatureMap[T][]> = new BehaviorSubject([])
+
+  @Output()
+  busy$: BehaviorSubject<boolean> = new BehaviorSubject(false)
+
+  constructor(
+    private sapi: SAPI
+  ) { }
+
+  async ngOnChanges(changes: SimpleChanges) {
+    if (!this.featureType) {
+      console.warn(`featureType must be defined!`)
+    }
+    this.busy$.next(true)
+    const features = await this.sapi.getV3Features(this.featureType, {
+      query: {
+        parcellation_id: this.parcellation?.id,
+        space_id: this.template?.id,
+        region_id: this.region?.name,
+      }
+    }).toPromise()
+    this.busy$.next(false)
+    this.features.next(features as any[])
+  }
+}
diff --git a/src/features/index.ts b/src/features/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d99543461bfe83c8376f42512683005eeb8df5ac
--- /dev/null
+++ b/src/features/index.ts
@@ -0,0 +1 @@
+export { FeatureModule } from "./module"
\ No newline at end of file
diff --git a/src/features/list/list.component.html b/src/features/list/list.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..c50515f2580ecaa0d415a5e5e9b14fb7639aae3b
--- /dev/null
+++ b/src/features/list/list.component.html
@@ -0,0 +1,11 @@
+<mat-list>
+    <mat-list-item mat-ripple
+        *ngFor="let feature of features$ | async"
+        [matTooltip]="feature.name"
+        matTooltipPosition="right"
+        (click)="onClickItem(feature)">
+        <span class="feature-name">
+            {{ feature.name }}
+        </span>
+    </mat-list-item>
+</mat-list>
diff --git a/src/features/list/list.component.scss b/src/features/list/list.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..99448693600d945c9af3622013e7f196f900d221
--- /dev/null
+++ b/src/features/list/list.component.scss
@@ -0,0 +1,15 @@
+:host
+{
+    display: block;
+    width: 100%;
+}
+
+.feature-name
+{
+    white-space: nowrap;
+}
+
+.feature-name:hover
+{
+    cursor: default;
+}
diff --git a/src/features/list/list.component.spec.ts b/src/features/list/list.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..54ae348082a0c2157da9006173bec361526f64a8
--- /dev/null
+++ b/src/features/list/list.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ListComponent } from './list.component';
+
+describe('ListComponent', () => {
+  let component: ListComponent;
+  let fixture: ComponentFixture<ListComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ ListComponent ]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(ListComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/features/list/list.component.ts b/src/features/list/list.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9d81ab404dceaa2904b9435bbb0861ac1a32e3ca
--- /dev/null
+++ b/src/features/list/list.component.ts
@@ -0,0 +1,67 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { BehaviorSubject, combineLatest, NEVER, Observable, of, throwError } from 'rxjs';
+import { catchError, switchMap, tap } from 'rxjs/operators';
+import { SAPI } from 'src/atlasComponents/sapi';
+import { Feature } from 'src/atlasComponents/sapi/sxplrTypes';
+import { FeatureType } from 'src/atlasComponents/sapi/typeV3';
+import { AllFeatures, FeatureBase } from '../base';
+
+@Component({
+  selector: 'sxplr-feature-list',
+  templateUrl: './list.component.html',
+  styleUrls: ['./list.component.scss'],
+  exportAs: "featureList"
+})
+export class ListComponent extends FeatureBase {
+
+  @Output()
+  onClickFeature = new EventEmitter<Feature>()
+
+  @Input()
+  featureRoute: string
+  private guardedRoute$ = new BehaviorSubject<FeatureType>(null)
+
+  public state$ = new BehaviorSubject<'busy'|'noresult'|'result'>('noresult')
+
+  constructor(private sapi: SAPI) {
+    super()
+  }
+
+  ngOnChanges(): void {
+    super.ngOnChanges()
+    const featureType = (this.featureRoute || '').split("/").slice(-1)[0]
+    this.guardedRoute$.next(AllFeatures[featureType])
+  }
+
+  public features$: Observable<Feature[]> = combineLatest([
+    this.guardedRoute$,
+    this.TPR$,
+  ]).pipe(
+    tap(() => this.state$.next('busy')),
+    switchMap(([route, { template, parcellation, region }]) => {
+      if (!route) {
+        return throwError("noresult")
+      }
+      const query = {}
+      if (template) query['space_id'] = template.id
+      if (parcellation) query['parcellation_id'] = parcellation.id
+      if (region) query['region_id'] = region.name
+      return this.sapi.getV3Features(route, {
+        query: {
+          ...this.queryParams,
+          ...query,
+        } as any
+      }).pipe(
+      )
+    }),
+    catchError(() => {
+      this.state$.next("noresult")
+      return of([] as Feature[])
+    }),
+    tap(result => this.state$.next(result.length > 0 ? 'result' : 'noresult')),
+  )
+
+  onClickItem(feature: Feature){
+    this.onClickFeature.emit(feature)
+  }
+}
diff --git a/src/features/module.ts b/src/features/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..16214d8b14c72d81c28ffc8d3b1e5cc74eace3d0
--- /dev/null
+++ b/src/features/module.ts
@@ -0,0 +1,40 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { MatCardModule } from "@angular/material/card";
+import { MatRippleModule } from "@angular/material/core";
+import { MatExpansionModule } from "@angular/material/expansion";
+import { MatListModule } from "@angular/material/list";
+import { MatTooltipModule } from "@angular/material/tooltip";
+import { SpinnerModule } from "src/components/spinner";
+import { UtilModule } from "src/util";
+import { EntryComponent } from './entry/entry.component'
+import { FeatureNamePipe } from "./featureName.pipe";
+import { FetchDirective } from "./fetch.directive";
+import { ListComponent } from './list/list.component';
+import { CategoryAccDirective } from './category-acc.directive';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    MatCardModule,
+    MatExpansionModule,
+    SpinnerModule,
+    MatListModule,
+    MatTooltipModule,
+    UtilModule,
+    MatRippleModule,
+  ],
+  declarations: [
+    EntryComponent,
+    ListComponent,
+
+    FetchDirective,
+    CategoryAccDirective,
+
+    FeatureNamePipe,
+  ],
+  exports: [
+    EntryComponent,
+  ]
+})
+export class FeatureModule{}
\ No newline at end of file
diff --git a/src/overwrite.scss b/src/overwrite.scss
index 86f98096cead1a68e768e98ebd77aa084859fdf2..a60e2755623bed99f769435701c0798d47ce927d 100644
--- a/src/overwrite.scss
+++ b/src/overwrite.scss
@@ -153,7 +153,7 @@ $transform-origin-maps: (
   margin-bottom: auto!important;
 }
 
-$display-vars: none, block, inline-block, flex, inline-flex;
+$display-vars: none, block, inline-block, flex, inline-flex, grid;
 @each $display-var in $display-vars {
   .d-#{$display-var}
   {
diff --git a/src/routerModule/routeStateTransform.service.ts b/src/routerModule/routeStateTransform.service.ts
index c1e2460f8eccd65d3e28266a956966ccc3c9161f..8a6fe4d335e622fc62672716790827f3cb9ba2ba 100644
--- a/src/routerModule/routeStateTransform.service.ts
+++ b/src/routerModule/routeStateTransform.service.ts
@@ -39,7 +39,7 @@ export class RouteStateTransformSvc {
       allParcellationRegions = []
     ] = await Promise.all([
       this.sapi.atlases$.pipe(
-        map(atlases => atlases.find(atlas => atlas["@id"] === selectedAtlasId))
+        map(atlases => atlases.find(atlas => atlas.id === selectedAtlasId))
       ).toPromise(),
       this.sapi.v3Get("/spaces/{space_id}", {
         path: {
@@ -296,17 +296,17 @@ export class RouteStateTransformSvc {
     
     routes = {
       // for atlas
-      a: selectedAtlas && encodeId(selectedAtlas['@id']),
+      a: selectedAtlas && encodeId(selectedAtlas.id),
       // for template
-      t: selectedTemplate && encodeId(selectedTemplate['@id']),
+      t: selectedTemplate && encodeId(selectedTemplate.id),
       // for parcellation
-      p: selectedParcellation && encodeId(selectedParcellation['@id']),
+      p: selectedParcellation && encodeId(selectedParcellation.id),
       // for regions
       r: selectedRegionsString && encodeURIFull(selectedRegionsString),
       // nav
       ['@']: cNavString,
       // showing dataset
-      f: selectedFeature && encodeId(selectedFeature["@id"])
+      f: selectedFeature && encodeId(selectedFeature.id)
     }
   
     /**
diff --git a/src/state/atlasSelection/effects.ts b/src/state/atlasSelection/effects.ts
index 92442f0794a9c192674b96cf1d5fc855479f6da9..7b25c2e9a7ab8e5dc27171647702ee4276d67692 100644
--- a/src/state/atlasSelection/effects.ts
+++ b/src/state/atlasSelection/effects.ts
@@ -160,7 +160,6 @@ export class Effect {
             this.onTemplateParcSelectionPostHook.map(fn => fn({ previous: { atlas: currAtlas, template: currTmpl, parcellation: currParc }, current: { atlas, template, parcellation } }))
           ).pipe(
             map(partialStates => {
-              console.log('selected teplate', template, parcellation)
               let returnState: Partial<AtlasSelectionState> = {
                 selectedAtlas: atlas,
                 selectedTemplate: template,
diff --git a/src/util/priority.ts b/src/util/priority.ts
index b12abb243d9c984ea9f9d3ec7dd608d375357e9e..24cdd94725c8774bc937c5f0098218d3e7d07151 100644
--- a/src/util/priority.ts
+++ b/src/util/priority.ts
@@ -170,7 +170,7 @@ export class PriorityHttpInterceptor implements HttpInterceptor{
       filter(v => v.urlWithParams === urlWithParams),
       take(1),
       map(v => {
-        if (v instanceof Error) {
+        if (v['error'] instanceof Error) {
           throw v
         }
         return (v as Result<unknown>).result
diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html
index 0731f9bf2aad326458d20373fc7395d174f7763b..ce5992c136c461465922a8cca38ebcc19c8dedd5 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.template.html
+++ b/src/viewerModule/viewerCmp/viewerCmp.template.html
@@ -911,8 +911,8 @@
   let-backCb="backCb"
   let-feature="feature">
 
-  <sxplr-sapiviews-core-datasets-dataset class="sxplr-z-2 mat-elevation-z2"
-    [sxplr-sapiviews-core-datasets-dataset-input]="feature">
+  <sxplr-feature-view class="sxplr-z-2 mat-elevation-z2"
+    [feature]="feature">
 
     <div header>
       <!-- back btn -->
@@ -929,7 +929,7 @@
       </button>
     </div>
 
-  </sxplr-sapiviews-core-datasets-dataset>
+  </sxplr-feature-view>
   
   <!-- TODO FIXME -->
   <!-- <sxplr-sapiviews-features-entry