From 81082c8bf39cebed28803448b8431642a319ed33 Mon Sep 17 00:00:00 2001
From: esikkala <esko.ikkala@aalto.fi>
Date: Tue, 8 Jun 2021 11:17:48 +0300
Subject: [PATCH] Refactor layout - unify main level container styles - new
 layout config object - better support for small screens

---
 src/client/components/App.js                  |  12 +
 src/client/components/facet_bar/FacetBar.js   |  28 +-
 .../components/facet_bar/FacetHeader.js       |  13 +-
 src/client/components/facet_bar/FacetInfo.js  |  15 +-
 .../components/facet_results/ApexChart.js     |  10 +-
 src/client/components/facet_results/Deck.js   |  11 +-
 src/client/components/facet_results/Export.js |  10 +-
 .../components/facet_results/LeafletMap.js    |  31 +-
 .../components/facet_results/Network.js       |   7 +-
 .../components/facet_results/ResultTable.js   |  23 +-
 .../facet_results/VirtualizedTable.js         |   8 +-
 .../components/main_layout/InfoHeader.js      | 108 +--
 src/client/components/main_layout/Main.js     |  36 +-
 src/client/components/main_layout/MainCard.js |   9 +-
 .../components/main_layout/PerspectiveTabs.js |  18 +-
 .../sampo/FacetedSearchPerspective.js         |   3 +-
 .../components/perspectives/sampo/Footer.js   |  43 +-
 .../perspectives/sampo/InstanceHomePage.js    |  19 +-
 .../perspectives/sampo/Perspective1.js        |  17 +-
 .../perspectives/sampo/Perspective2.js        |   6 +-
 .../perspectives/sampo/Perspective3.js        |  12 +-
 .../components/perspectives/sampo/TopBar.js   |  46 +-
 .../sampo/client_fs/ClientFSMain.js           |  53 +-
 .../sampo/client_fs/ClientFSPerspective.js    |  25 +-
 src/client/configs/sampo/GeneralConfig.js     |  40 +-
 .../configs/sampo/Leaflet/LeafletConfig.js    |   3 +
 src/client/configs/sampo/PerspectiveConfig.js |   9 +-
 src/client/containers/SemanticPortal.js       | 696 +++++++++---------
 src/client/index.css                          |  17 +-
 29 files changed, 730 insertions(+), 598 deletions(-)

diff --git a/src/client/components/App.js b/src/client/components/App.js
index 07346cad..f9755774 100644
--- a/src/client/components/App.js
+++ b/src/client/components/App.js
@@ -21,6 +21,18 @@ const theme = createMuiTheme({
         }
       }
     },
+    MuiAccordionSummary: {
+      content: {
+        '&$expanded': {
+          marginTop: 4
+        }
+      },
+      expandIcon: {
+        '&$expanded': {
+          marginTop: -16
+        }
+      }
+    },
     MuiButton: {
       endIcon: {
         marginLeft: 0
diff --git a/src/client/components/facet_bar/FacetBar.js b/src/client/components/facet_bar/FacetBar.js
index 12ce8e3d..57b65ae0 100644
--- a/src/client/components/facet_bar/FacetBar.js
+++ b/src/client/components/facet_bar/FacetBar.js
@@ -30,10 +30,14 @@ const styles = theme => ({
     borderBottomLeftRadius: 0,
     borderBottomRightRadius: 0
   },
-  accordionSummaryRoot: {
+  accordionSummaryRoot: props => ({
     paddingLeft: theme.spacing(1),
-    cursor: 'default !important'
-  },
+    cursor: 'default !important',
+    minHeight: 38,
+    [theme.breakpoints.up(props.layoutConfig.reducedHeightBreakpoint)]: {
+      minHeight: 48
+    }
+  }),
   accordionSummaryContent: {
     margin: 0
   },
@@ -73,7 +77,7 @@ class FacetBar extends React.Component {
     }
   }
 
-  handleExpandButtonOnClick = facetID => () => {
+  handleExpandButtonOnClick = facetID => event => {
     const activeFacets = this.state.activeFacets
     if (activeFacets.has(facetID)) {
       activeFacets.delete(facetID)
@@ -244,6 +248,7 @@ class FacetBar extends React.Component {
       <Accordion
         key={facetID}
         expanded={isActive}
+        // onClick={this.handleExpandButtonOnClick(facetID)}
       >
         <AccordionSummary
           classes={{
@@ -273,6 +278,7 @@ class FacetBar extends React.Component {
             updateFacetOption={this.props.updateFacetOption}
             facetDescription={description}
             rootUrl={this.props.rootUrl}
+            layoutConfig={this.props.layoutConfig}
           />
         </AccordionSummary>
         <AccordionDetails
@@ -284,6 +290,15 @@ class FacetBar extends React.Component {
     )
   }
 
+  getTypographyVariant = () => {
+    const { screenSize } = this.props
+    let variant = 'h6'
+    if (screenSize === 'xs' || screenSize === 'sm' || screenSize === 'md') {
+      variant = 'subtitle2'
+    }
+    return variant
+  }
+
   renderFacets = ({ classes, facets, someFacetIsFetching }) => {
     const { screenSize } = this.props
     if (screenSize === 'xs' || screenSize === 'sm') {
@@ -298,7 +313,7 @@ class FacetBar extends React.Component {
             aria-controls='panel1a-content'
             id='panel1a-header'
           >
-            <Typography variant='h6'>{intl.get('facetBar.filters')}</Typography>
+            <Typography variant={this.getTypographyVariant()}>{intl.get('facetBar.filters')}</Typography>
           </AccordionSummary>
           <AccordionDetails
             className={classes.accordionDetails}
@@ -375,7 +390,8 @@ class FacetBar extends React.Component {
               screenSize={this.props.screenSize}
             />
           </Paper>}
-        {this.renderFacets({ classes, facets, someFacetIsFetching })}
+        {(facetedSearchMode === 'serverFS' || facetData.results !== null) &&
+          this.renderFacets({ classes, facets, someFacetIsFetching })}
       </div>
     )
   }
diff --git a/src/client/components/facet_bar/FacetHeader.js b/src/client/components/facet_bar/FacetHeader.js
index 81170948..65a56179 100644
--- a/src/client/components/facet_bar/FacetHeader.js
+++ b/src/client/components/facet_bar/FacetHeader.js
@@ -30,6 +30,12 @@ const styles = theme => ({
     // justifyContent: 'space-between',
     width: '100%'
   },
+  facetLabel: props => ({
+    fontSize: '0.875rem',
+    [theme.breakpoints.up(props.layoutConfig.reducedHeightBreakpoint)]: {
+      fontSize: '1rem'
+    }
+  }),
   facetValuesContainerTen: {
     height: 345,
     padding: theme.spacing(1)
@@ -55,6 +61,7 @@ class FacetHeader extends React.Component {
   }
 
   handleMenuButtonClick = event => {
+    event.stopPropagation()
     this.setState({ anchorEl: event.currentTarget })
   };
 
@@ -332,6 +339,7 @@ class FacetHeader extends React.Component {
           <>
             <Tooltip disableFocusListener title={intl.get('facetBar.filterOptions')}>
               <IconButton
+                className='facetMenuButton'
                 aria-label={intl.get('facetBar.filterOptions')}
                 aria-owns={open ? 'facet-option-menu' : undefined}
                 aria-haspopup='true'
@@ -361,7 +369,7 @@ class FacetHeader extends React.Component {
 
     return (
       <div className={classes.headingContainer}>
-        <Typography variant='body1'>{facetLabel} </Typography>
+        <Typography className={classes.facetLabel} variant='body1'>{facetLabel}</Typography>
         <Tooltip
           title={facetDescription}
           enterDelay={300}
@@ -396,7 +404,8 @@ FacetHeader.propTypes = {
   clearFacet: PropTypes.func,
   updateFacetOption: PropTypes.func,
   facetDescription: PropTypes.string.isRequired,
-  rootUrl: PropTypes.string.isRequired
+  rootUrl: PropTypes.string.isRequired,
+  layoutConfig: PropTypes.object.isRequired
 }
 
 export const FacetHeaderComponent = FacetHeader
diff --git a/src/client/components/facet_bar/FacetInfo.js b/src/client/components/facet_bar/FacetInfo.js
index 660753a5..50afa37a 100644
--- a/src/client/components/facet_bar/FacetInfo.js
+++ b/src/client/components/facet_bar/FacetInfo.js
@@ -49,6 +49,15 @@ class FacetInfo extends React.Component {
 
   handleRemoveAllFiltersOnClick = () => this.props.clearAllFacets({ facetClass: this.props.facetClass })
 
+  getTypographyVariant = () => {
+    const { screenSize } = this.props
+    let variant = 'h6'
+    if (screenSize === 'xs' || screenSize === 'sm' || screenSize === 'md') {
+      variant = 'subtitle2'
+    }
+    return variant
+  }
+
   render () {
     const { classes, facetClass, resultClass, resultCount, someFacetIsFetching, screenSize } = this.props
     const mobileMode = screenSize === 'xs' || screenSize === 'sm'
@@ -90,7 +99,7 @@ class FacetInfo extends React.Component {
       <div className={classes.root}>
         {this.props.fetchingResultCount
           ? <CircularProgress style={{ color: purple[500] }} thickness={5} size={26} />
-          : <Typography variant='h6'>{intl.get('facetBar.results')}: {resultCount} {intl.get(`perspectives.${resultClass}.facetResultsType`)}</Typography>}
+          : <Typography variant={this.getTypographyVariant()}>{intl.get('facetBar.results')}: {resultCount} {intl.get(`perspectives.${resultClass}.facetResultsType`)}</Typography>}
         {!mobileMode && <Divider className={classes.facetInfoDivider} />}
         {(activeUriFilters ||
           activeSpatialFilters ||
@@ -100,7 +109,7 @@ class FacetInfo extends React.Component {
         ) &&
           <>
             <div className={classes.headerContainer}>
-              <Typography variant='h6'>{intl.get('facetBar.activeFilters')}</Typography>
+              <Typography variant={this.getTypographyVariant()}>{intl.get('facetBar.activeFilters')}</Typography>
               <Button
                 variant='contained'
                 color='secondary'
@@ -128,7 +137,7 @@ class FacetInfo extends React.Component {
             </div>
             <Divider className={classes.facetInfoDivider} />
           </>}
-        {!mobileMode && <Typography variant='h6'>{intl.get('facetBar.narrowDownBy')}:</Typography>}
+        {!mobileMode && <Typography variant={this.getTypographyVariant()}>{intl.get('facetBar.narrowDownBy')}:</Typography>}
       </div>
     )
   }
diff --git a/src/client/components/facet_results/ApexChart.js b/src/client/components/facet_results/ApexChart.js
index bdcfe759..d8debd88 100644
--- a/src/client/components/facet_results/ApexChart.js
+++ b/src/client/components/facet_results/ApexChart.js
@@ -12,6 +12,9 @@ import Typography from '@material-ui/core/Typography'
 import GeneralDialog from '../main_layout/GeneralDialog'
 import InstaceList from '../main_layout/InstanceList'
 
+const defaultPadding = 32
+const smallScreenPadding = 8
+
 const styles = theme => ({
   selectContainer: {
     display: 'flex',
@@ -139,7 +142,7 @@ class ApexChart extends React.Component {
     if (this.isSmallScreen()) {
       return 'auto'
     }
-    const rootHeightReduction = 136 // tabs + padding
+    const rootHeightReduction = this.props.layoutConfig.tabHeight + 2 * defaultPadding + 1
     return `calc(100% - ${rootHeightReduction}px)`
   }
 
@@ -172,7 +175,7 @@ class ApexChart extends React.Component {
       height: '100%'
     }
     if (pageType === 'facetResults' || pageType === 'instancePage') {
-      const padding = this.isSmallScreen() ? 8 : 32
+      const padding = this.isSmallScreen() ? smallScreenPadding : defaultPadding
       rootStyle = {
         height: this.getHeightForRootContainer(),
         width: `calc(100% - ${2 * padding}px)`,
@@ -269,7 +272,8 @@ ApexChart.propTypes = {
   uri: PropTypes.string,
   dropdownForResultClasses: PropTypes.bool,
   facetResultsType: PropTypes.string,
-  resultClasses: PropTypes.array
+  resultClasses: PropTypes.array,
+  layoutConfig: PropTypes.object.isRequired
 }
 
 export const ApexChartComponent = ApexChart
diff --git a/src/client/components/facet_results/Deck.js b/src/client/components/facet_results/Deck.js
index ca5b4c8f..0aaf8fb5 100644
--- a/src/client/components/facet_results/Deck.js
+++ b/src/client/components/facet_results/Deck.js
@@ -20,12 +20,12 @@ import { purple } from '@material-ui/core/colors'
 */
 
 const styles = theme => ({
-  root: {
+  root: props => ({
     height: 400,
-    [theme.breakpoints.up('md')]: {
-      height: 'calc(100% - 72px)'
+    [theme.breakpoints.up(props.layoutConfig.hundredPercentHeightBreakPoint)]: {
+      height: `calc(100% - ${props.layoutConfig.tabHeight}px)`
     }
-  },
+  }),
   spinner: {
     height: 40,
     width: 40,
@@ -298,7 +298,8 @@ Deck.propTypes = {
   legendTitle: PropTypes.string,
   showMoreText: PropTypes.string,
   listHeadingSingleInstance: PropTypes.string,
-  listHeadingMultipleInstances: PropTypes.string
+  listHeadingMultipleInstances: PropTypes.string,
+  layoutConfig: PropTypes.object.isRequired
 }
 
 export const DeckComponent = Deck
diff --git a/src/client/components/facet_results/Export.js b/src/client/components/facet_results/Export.js
index 4e6775cb..d493e3b0 100644
--- a/src/client/components/facet_results/Export.js
+++ b/src/client/components/facet_results/Export.js
@@ -8,13 +8,15 @@ import querystring from 'querystring'
 import intl from 'react-intl-universal'
 
 const styles = theme => ({
-  root: {
-    height: 'calc(100% - 72px)',
-    width: '100%',
+  root: props => ({
+    minHeight: 400,
+    [theme.breakpoints.up(props.layoutConfig.hundredPercentHeightBreakPoint)]: {
+      height: `calc(100% - ${props.layoutConfig.tabHeight}px)`
+    },
     display: 'flex',
     alignItems: 'center',
     justifyContent: 'center'
-  },
+  }),
   link: {
     textDecoration: 'none'
   },
diff --git a/src/client/components/facet_results/LeafletMap.js b/src/client/components/facet_results/LeafletMap.js
index fa42f32f..e032b704 100644
--- a/src/client/components/facet_results/LeafletMap.js
+++ b/src/client/components/facet_results/LeafletMap.js
@@ -8,7 +8,6 @@ import buffer from '@turf/buffer'
 import CircularProgress from '@material-ui/core/CircularProgress'
 import { purple } from '@material-ui/core/colors'
 import history from '../../History'
-import { MAPBOX_ACCESS_TOKEN, MAPBOX_STYLE } from '../../configs/sampo/GeneralConfig'
 // import { apiUrl } from '../../epics'
 import 'leaflet/dist/leaflet.css' // Official Leaflet styles
 
@@ -41,27 +40,27 @@ import markerIconOrange from '../../img/markers/marker-icon-orange.png'
 import markerIconYellow from '../../img/markers/marker-icon-yellow.png'
 
 const styles = theme => ({
-  leafletContainerfacetResults: {
+  leafletContainerfacetResults: props => ({
     height: 400,
-    [theme.breakpoints.up('md')]: {
-      height: 'calc(100% - 72px)'
+    [theme.breakpoints.up(props.layoutConfig.hundredPercentHeightBreakPoint)]: {
+      height: `calc(100% - ${props.layoutConfig.tabHeight}px)`
     },
     position: 'relative'
-  },
-  leafletContainerclientFSResults: {
+  }),
+  leafletContainerclientFSResults: props => ({
     height: 400,
-    [theme.breakpoints.up('md')]: {
-      height: 'calc(100% - 72px)'
+    [theme.breakpoints.up(props.layoutConfig.hundredPercentHeightBreakPoint)]: {
+      height: `calc(100% - ${props.layoutConfig.tabHeight}px)`
     },
     position: 'relative'
-  },
-  leafletContainerinstancePage: {
+  }),
+  leafletContainerinstancePage: props => ({
     height: 400,
-    [theme.breakpoints.up('md')]: {
+    [theme.breakpoints.up(props.layoutConfig.hundredPercentHeightBreakPoint)]: {
       height: '100%'
     },
     position: 'relative'
-  },
+  }),
   leafletContainermobileMapPage: {
     height: '100%',
     position: 'relative'
@@ -273,7 +272,7 @@ class LeafletMap extends React.Component {
 
   initMap = () => {
     // Base layer(s)
-    const mapboxBaseLayer = L.tileLayer(`https://api.mapbox.com/styles/v1/mapbox/${MAPBOX_STYLE}/tiles/{z}/{x}/{y}?access_token=${MAPBOX_ACCESS_TOKEN}`, {
+    const mapboxBaseLayer = L.tileLayer(`https://api.mapbox.com/styles/v1/mapbox/${this.props.mapBoxStyle}/tiles/{z}/{x}/{y}?access_token=${this.props.mapBoxAccessToken}`, {
       attribution: '&copy; <a href="https://www.mapbox.com/map-feedback/">Mapbox</a> &copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
       tileSize: 512,
       zoomOffset: -1
@@ -341,7 +340,7 @@ class LeafletMap extends React.Component {
     // initialize layers from external sources
     if (this.props.showExternalLayers) {
       const basemaps = {
-        [intl.get(`leafletMap.basemaps.mapbox.${MAPBOX_STYLE}`)]: mapboxBaseLayer
+        [intl.get(`leafletMap.basemaps.mapbox.${this.props.mapBoxStyle}`)]: mapboxBaseLayer
         // [intl.get('leafletMap.basemaps.backgroundMapNLS')]: backgroundMapNLS,
         // [intl.get('leafletMap.basemaps.topographicalMapNLS')]: topographicalMapNLS,
         // [intl.get('leafletMap.basemaps.airMapNLS')]: airMapNLS
@@ -1015,7 +1014,9 @@ LeafletMap.propTypes = {
   facetedSearchMode: PropTypes.string,
   container: PropTypes.string,
   showError: PropTypes.func,
-  uri: PropTypes.string
+  uri: PropTypes.string,
+  mapBoxStyle: PropTypes.string,
+  mapBoxAccessToken: PropTypes.string
 }
 
 export const LeafletMapComponent = LeafletMap
diff --git a/src/client/components/facet_results/Network.js b/src/client/components/facet_results/Network.js
index bd078753..7bd83bf8 100644
--- a/src/client/components/facet_results/Network.js
+++ b/src/client/components/facet_results/Network.js
@@ -130,7 +130,7 @@ class Network extends React.Component {
   }
 
   render = () => {
-    const { fetching, pageType } = this.props
+    const { fetching, pageType, layoutConfig } = this.props
     const rootStyle = {
       width: '100%',
       backgroundColor: '#fff',
@@ -140,7 +140,7 @@ class Network extends React.Component {
       rootStyle.height = 'calc(100% - 1px)'
     }
     if (pageType === 'facetResults') {
-      rootStyle.height = 'calc(100% - 72px)'
+      rootStyle.height = `calc(100% - ${layoutConfig.tabHeight + 1}px)`
     }
     const spinnerContainerStyle = {
       display: 'flex',
@@ -176,7 +176,8 @@ Network.propTypes = {
   layout: PropTypes.object,
   preprocess: PropTypes.func,
   fetching: PropTypes.bool,
-  fitLayout: PropTypes.bool
+  fitLayout: PropTypes.bool,
+  layoutConfig: PropTypes.object.isRequired
 }
 
 export default Network
diff --git a/src/client/components/facet_results/ResultTable.js b/src/client/components/facet_results/ResultTable.js
index 9461af64..eccdc67c 100644
--- a/src/client/components/facet_results/ResultTable.js
+++ b/src/client/components/facet_results/ResultTable.js
@@ -19,31 +19,32 @@ import ResultTablePaginationActions from './ResultTablePaginationActions'
 import history from '../../History'
 
 const styles = theme => ({
-  tableContainer: {
+  tableContainer: props => ({
     overflow: 'auto',
-    width: '100%',
-    height: 'auto',
-    [theme.breakpoints.up('md')]: {
-      height: 'calc(100% - 126px)'
+    [theme.breakpoints.up(props.layoutConfig.hundredPercentHeightBreakPoint)]: {
+      height: `calc(100% - ${props.layoutConfig.tabHeight + props.layoutConfig.paginationToolbarHeight + 2}px)`
     },
     backgroundColor: theme.palette.background.paper,
     borderTop: '1px solid rgba(224, 224, 224, 1);'
-  },
+  }),
   paginationRoot: {
     display: 'flex',
     backgroundColor: '#fff',
-    borderTop: '1px solid rgba(224, 224, 224, 1);'
+    borderTop: '1px solid rgba(224, 224, 224, 1);',
+    alignItems: 'center'
   },
   paginationCaption: {
     minWidth: 110
   },
-  paginationToolbar: {
-    [theme.breakpoints.down('xs')]: {
+  paginationToolbar: props => ({
+    '& p': { fontSize: '0.75rem' },
+    minHeight: props.layoutConfig.paginationToolbarHeight,
+    [theme.breakpoints.down(480)]: {
       display: 'flex',
       flexWrap: 'wrap',
-      height: 100
+      height: 60
     }
-  },
+  }),
   progressContainer: {
     width: '100%',
     height: 'calc(100% - 72px)',
diff --git a/src/client/components/facet_results/VirtualizedTable.js b/src/client/components/facet_results/VirtualizedTable.js
index bed03ebd..b3b4eb4a 100644
--- a/src/client/components/facet_results/VirtualizedTable.js
+++ b/src/client/components/facet_results/VirtualizedTable.js
@@ -17,9 +17,11 @@ import {
 // https://github.com/bvaughn/react-virtualized/blob/master/docs/usingAutoSizer.md
 
 const styles = theme => ({
-  root: {
+  root: props => ({
     display: 'flex',
-    height: 'calc(100% - 74px)',
+    [theme.breakpoints.up(props.layoutConfig.hundredPercentHeightBreakPoint)]: {
+      height: `calc(100% - ${props.layoutConfig.tabHeight}px)`
+    },
     // width: 'calc(100% - 1px)',
     flexGrow: 1,
     borderTop: '1px solid rgb(224, 224, 224)',
@@ -27,7 +29,7 @@ const styles = theme => ({
     '& a': {
       textDecoration: 'underline'
     }
-  },
+  }),
   resultsInfo: {
     flexGrow: 0
   }
diff --git a/src/client/components/main_layout/InfoHeader.js b/src/client/components/main_layout/InfoHeader.js
index 5c6d423e..ab6f1e3c 100644
--- a/src/client/components/main_layout/InfoHeader.js
+++ b/src/client/components/main_layout/InfoHeader.js
@@ -1,64 +1,81 @@
 import React from 'react'
 import PropTypes from 'prop-types'
-import { withStyles } from '@material-ui/core/styles'
+import { makeStyles } from '@material-ui/core/styles'
 import Accordion from '@material-ui/core/Accordion'
 import AccordionSummary from '@material-ui/core/AccordionSummary'
 import AccordionDetails from '@material-ui/core/AccordionDetails'
 import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
 import Typography from '@material-ui/core/Typography'
-import Grid from '@material-ui/core/Grid'
 // import Divider from '@material-ui/core/Divider';
 import IconButton from '@material-ui/core/IconButton'
 import InfoIcon from '@material-ui/icons/InfoOutlined'
 import intl from 'react-intl-universal'
 
-const styles = theme => ({
+const useStyles = makeStyles(theme => ({
   root: {
-    position: 'absolute',
-    // marginTop: 64,
-    paddingTop: theme.spacing(1),
-    paddingLeft: theme.spacing(1.5),
-    paddingRight: theme.spacing(1.5),
-    [theme.breakpoints.down('sm')]: {
-      marginTop: 56
-    },
-    [theme.breakpoints.up('sm')]: {
-      marginTop: 64
-    }
+    marginTop: theme.spacing(0.5),
+    marginLeft: theme.spacing(0.5),
+    marginRight: theme.spacing(0.5)
   },
   panel: {
     width: '100%'
   },
-  summary: {
-    paddingLeft: theme.spacing(1)
-  },
+  summary: props => ({
+    paddingLeft: theme.spacing(1),
+    [theme.breakpoints.down(props.layoutConfig.reducedHeightBreakpoint)]: {
+      minHeight: `${props.layoutConfig.infoHeader.reducedHeight.height}px !important`
+    },
+    [theme.breakpoints.up(props.layoutConfig.reducedHeightBreakpoint)]: {
+      minHeight: `${props.layoutConfig.infoHeader.default.height}px !important`
+    }
+  }),
   summaryContent: {
     display: 'block',
-    marginBottom: `${theme.spacing(1)}px !important`
+    marginTop: theme.spacing(0.5),
+    marginBottom: `${theme.spacing(0.5)}px !important`
   },
   headingContainer: {
     display: 'flex'
   },
+  heading: {
+    flexShrink: 1
+  },
   infoIconButton: {
+    padding: 0,
     marginLeft: theme.spacing(0.5)
   },
-  infoIcon: {
-    fontSize: 32
-  },
+  infoIcon: props => ({
+    [theme.breakpoints.down(props.layoutConfig.reducedHeightBreakpoint)]: {
+      fontSize: props.layoutConfig.infoHeader.reducedHeight.infoIconFontSize
+    },
+    [theme.breakpoints.up(props.layoutConfig.reducedHeightBreakpoint)]: {
+      fontSize: props.layoutConfig.infoHeader.default.infoIconFontSize
+    }
+  }),
   label: {
     marginTop: theme.spacing(1),
     height: 32,
     overflow: 'auto'
   },
-  content: {
+  expandedContent: props => ({
+    '& p, & ul': {
+      fontSize: '0.875rem'
+    },
+    height: props.layoutConfig.infoHeader.reducedHeight.expandedContentHeight,
+    [theme.breakpoints.up(props.layoutConfig.reducedHeightBreakpoint)]: {
+      height: props.layoutConfig.infoHeader.default.expandedContentHeight,
+      '& p, & ul': {
+        fontSize: '1rem'
+      }
+    },
     paddingTop: 0,
     paddingLeft: theme.spacing(1),
     paddingBottom: theme.spacing(1),
     marginBottom: theme.spacing(1),
     overflow: 'auto',
     display: 'block'
-  }
-})
+  })
+}))
 
 /**
  * A component for instructions for a faceted search perspective or an entity landing page.
@@ -79,38 +96,46 @@ const InfoHeader = props => {
     return label
   }
 
+  const getHeadingVariant = () => {
+    const { screenSize } = props
+    let variant = props.layoutConfig.infoHeader.default.headingVariant
+    if (screenSize === 'xs' || screenSize === 'sm' || screenSize === 'md') {
+      variant = props.layoutConfig.infoHeader.reducedHeight.headingVariant
+    }
+    return variant
+  }
+
+  const classes = useStyles(props)
+
   return (
-    <Grid container spacing={1} className={props.classes.root}>
+    <div className={classes.root}>
       <Accordion
-        className={props.classes.panel}
+        className={classes.panel}
         expanded={props.expanded}
       >
         <AccordionSummary
-          className={props.classes.summary}
+          className={classes.summary}
           classes={{
-            content: props.classes.summaryContent
+            content: classes.summaryContent
           }}
           expandIcon={<ExpandMoreIcon />}
           aria-controls='panel1a-content'
           id='panel1a-header'
           IconButtonProps={{ onClick: handleExpandButtonOnClick }}
         >
-          <div className={props.classes.headingContainer}>
-            <Typography component='h1' variant='h4'>
+          <div className={classes.headingContainer}>
+            <Typography component='h1' variant={getHeadingVariant()} className={classes.heading}>
               {props.pageType === 'facetResults' && intl.get(`perspectives.${props.resultClass}.label`)}
               {props.pageType === 'instancePage' && intl.get(`perspectives.${props.resultClass}.instancePage.label`)}
             </Typography>
-            <IconButton className={props.classes.infoIconButton} onClick={handleExpandButtonOnClick}>
-              <InfoIcon className={props.classes.infoIcon} />
+            <IconButton className={classes.infoIconButton} onClick={handleExpandButtonOnClick}>
+              <InfoIcon className={classes.infoIcon} />
             </IconButton>
           </div>
           {props.pageType === 'instancePage' &&
-            <Typography className={props.classes.label} component='h1' variant='h6'>{generateLabel()}</Typography>}
+            <Typography className={classes.label} component='h1' variant='h6'>{generateLabel()}</Typography>}
         </AccordionSummary>
-        <AccordionDetails
-          className={props.classes.content}
-          style={{ height: props.descriptionHeight }}
-        >
+        <AccordionDetails className={classes.expandedContent}>
           {props.pageType === 'facetResults' && intl.getHTML(`perspectives.${props.resultClass}.longDescription`)}
           {props.pageType === 'instancePage' &&
             <>
@@ -121,20 +146,17 @@ const InfoHeader = props => {
             </>}
         </AccordionDetails>
       </Accordion>
-    </Grid>
+    </div>
   )
 }
 
 InfoHeader.propTypes = {
-  classes: PropTypes.object.isRequired,
   resultClass: PropTypes.string.isRequired,
   instanceData: PropTypes.object,
   pageType: PropTypes.string.isRequired,
   expanded: PropTypes.bool.isRequired,
   updateExpanded: PropTypes.func.isRequired,
-  descriptionHeight: PropTypes.number.isRequired
+  layoutConfig: PropTypes.object.isRequired
 }
 
-export const InfoHeaderComponent = InfoHeader
-
-export default withStyles(styles)(InfoHeader)
+export default InfoHeader
diff --git a/src/client/components/main_layout/Main.js b/src/client/components/main_layout/Main.js
index 37f882b0..7a019ee0 100644
--- a/src/client/components/main_layout/Main.js
+++ b/src/client/components/main_layout/Main.js
@@ -5,32 +5,33 @@ import Grid from '@material-ui/core/Grid'
 import Typography from '@material-ui/core/Typography'
 import { makeStyles } from '@material-ui/core/styles'
 import MainCard from './MainCard'
-import bannerImage from '../../img/main_page/mmm-banner.jpg'
 
 const useStyles = makeStyles(theme => ({
-  root: {
-    width: '100%',
-    marginBottom: theme.spacing(1),
-    [theme.breakpoints.up('md')]: {
-      height: 'calc(100% - 72px)',
-      overflow: 'auto'
-    }
-  },
-  banner: {
-    background: `linear-gradient( rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0.45) ), url(${bannerImage})`,
+  root: props => ({
+    [theme.breakpoints.up(props.layoutConfig.hundredPercentHeightBreakPoint)]: {
+      overflow: 'auto',
+      height: `calc(100% - ${props.layoutConfig.topBar.reducedHeight + props.layoutConfig.footer.height + theme.spacing(3.5)}px)`
+    },
+    [theme.breakpoints.up(props.layoutConfig.reducedHeightBreakpoint)]: {
+      overflow: 'auto',
+      height: `calc(100% - ${props.layoutConfig.topBar.defaultHeight + props.layoutConfig.footer.height + theme.spacing(3.5)}px)`
+    },
+    marginBottom: theme.spacing(5)
+  }),
+  banner: props => ({
+    background: props.layoutConfig.mainPage.bannerBackround,
     backgroundRepeat: 'no-repeat',
     backgroundSize: 'cover',
     backgroundPosition: 'center',
-    height: 220,
+    height: props.layoutConfig.mainPage.bannerReducedHeight,
     [theme.breakpoints.up('xl')]: {
-      height: 300
+      height: props.layoutConfig.mainPage.bannerDefaultHeight
     },
-    width: '100%',
     boxShadow: '0 -15px 15px 0px #bdbdbd inset',
     display: 'flex',
     alignItems: 'center',
     justifyContent: 'center'
-  },
+  }),
   bannerContent: {
     display: 'inline-block',
     color: '#fff'
@@ -44,9 +45,8 @@ const useStyles = makeStyles(theme => ({
     }
   },
   layout: {
-    width: 'auto',
-    marginLeft: theme.spacing(3),
-    marginRight: theme.spacing(3),
+    marginLeft: theme.spacing(1),
+    marginRight: theme.spacing(1),
     [theme.breakpoints.up(800 + theme.spacing(6))]: {
       width: 800,
       marginLeft: 'auto',
diff --git a/src/client/components/main_layout/MainCard.js b/src/client/components/main_layout/MainCard.js
index 6bda297d..8db51c63 100644
--- a/src/client/components/main_layout/MainCard.js
+++ b/src/client/components/main_layout/MainCard.js
@@ -21,6 +21,10 @@ const useStyles = makeStyles(theme => ({
       justifyContent: 'center'
     },
     height: 228,
+    [theme.breakpoints.down('sm')]: {
+      height: 170,
+      maxWidth: 300
+    },
     [props.perspective.frontPageElement === 'card']: {
       height: 'inherit',
       maxWidth: 269,
@@ -42,10 +46,7 @@ const useStyles = makeStyles(theme => ({
       backgroundPosition: 'center'
     },
     height: '100%',
-    width: '100%',
-    [theme.breakpoints.down('xs')]: {
-      width: '75%'
-    }
+    width: '100%'
   }),
   cardMedia: {
     height: 100
diff --git a/src/client/components/main_layout/PerspectiveTabs.js b/src/client/components/main_layout/PerspectiveTabs.js
index b9c85339..ab0327cc 100644
--- a/src/client/components/main_layout/PerspectiveTabs.js
+++ b/src/client/components/main_layout/PerspectiveTabs.js
@@ -7,12 +7,19 @@ import { Link } from 'react-router-dom'
 import Paper from '@material-ui/core/Paper'
 import intl from 'react-intl-universal'
 
-const styles = () => ({
+const styles = theme => ({
   root: {
     flexGrow: 1,
     borderBottomLeftRadius: 0,
     borderBottomRightRadius: 0
-  }
+  },
+  tabRoot: {
+    paddingTop: theme.spacing(0.5),
+    paddingBottom: theme.spacing(0.5)
+  },
+  tabLabelIcon: props => ({
+    minHeight: props.layoutConfig.tabHeight
+  })
 })
 
 /**
@@ -65,6 +72,10 @@ class PerspectiveTabs extends React.Component {
         >
           {tabs.map(tab =>
             <Tab
+              classes={{
+                root: classes.tabRoot,
+                labelIcon: classes.tabLabelIcon
+              }}
               key={tab.id}
               icon={tab.icon}
               label={intl.get(`tabs.${tab.id}`)}
@@ -83,7 +94,8 @@ PerspectiveTabs.propTypes = {
   classes: PropTypes.object.isRequired,
   routeProps: PropTypes.object.isRequired,
   tabs: PropTypes.array.isRequired,
-  screenSize: PropTypes.string.isRequired
+  screenSize: PropTypes.string.isRequired,
+  layoutConfig: PropTypes.object.isRequired
 }
 
 export const PerspectiveTabsComponent = PerspectiveTabs
diff --git a/src/client/components/perspectives/sampo/FacetedSearchPerspective.js b/src/client/components/perspectives/sampo/FacetedSearchPerspective.js
index 58059076..8998dc5e 100644
--- a/src/client/components/perspectives/sampo/FacetedSearchPerspective.js
+++ b/src/client/components/perspectives/sampo/FacetedSearchPerspective.js
@@ -139,7 +139,8 @@ FacetedSearchPerspective.propTypes = {
   /**
    * Root url of the application.
    */
-  rootUrl: PropTypes.string.isRequired
+  rootUrl: PropTypes.string.isRequired,
+  layoutConfig: PropTypes.object.isRequired
 }
 
 export default FacetedSearchPerspective
diff --git a/src/client/components/perspectives/sampo/Footer.js b/src/client/components/perspectives/sampo/Footer.js
index 3466d28e..8adb70ca 100644
--- a/src/client/components/perspectives/sampo/Footer.js
+++ b/src/client/components/perspectives/sampo/Footer.js
@@ -2,28 +2,22 @@ import React from 'react'
 import Paper from '@material-ui/core/Paper'
 import PropTypes from 'prop-types'
 import Grid from '@material-ui/core/Grid'
-import { withStyles } from '@material-ui/core/styles'
+import { makeStyles } from '@material-ui/core/styles'
 import aaltoLogo from '../../../img/logos/Aalto_SCI_EN_13_BLACK_2_cropped.png'
 import hyLogo from '../../../img/logos/university-of-helsinki-logo-transparent-black.png'
 import heldigLogo from '../../../img/logos/heldig-logo-transparent-black.png'
 
-const styles = theme => ({
+const useStyles = makeStyles(theme => ({
   root: {
-    position: 'absolute',
-    [theme.breakpoints.down('sm')]: {
-      display: 'none'
-    },
-    bottom: 0,
-    left: 0,
+    display: 'block',
     boxShadow: '0 -20px 20px -20px #333',
-    width: '100%',
     borderRadius: 0
   },
-  layout: {
-    width: 'auto',
-    // height: 115, for two row footer
-    marginTop: theme.spacing(1),
-    marginBottom: theme.spacing(1),
+  layout: props => ({
+    [theme.breakpoints.up(props.layoutConfig.hundredPercentHeightBreakPoint)]: {
+      height: props.layoutConfig.footer.height
+      // height: 115, for two row footer
+    },
     marginLeft: theme.spacing(2),
     marginRight: theme.spacing(2),
     [theme.breakpoints.up(1500 + theme.spacing(6))]: {
@@ -31,7 +25,7 @@ const styles = theme => ({
       marginLeft: 'auto',
       marginRight: 'auto'
     }
-  },
+  }),
   logoContainer: {
     display: 'flex',
     alignItems: 'center',
@@ -49,17 +43,17 @@ const styles = theme => ({
   heldigLogo: {
     height: 33
   }
-})
+}))
 
 /**
  * A component for creating a footer. The logos are imported inside this component.
  */
 const Footer = props => {
-  const { classes } = props
+  const classes = useStyles(props)
   return (
     <Paper className={classes.root}>
-      <Grid container className={classes.layout}>
-        <Grid container spacing={3} item xs={12}>
+      <div className={classes.layout}>
+        <Grid container spacing={3}>
           <Grid item xs className={classes.logoContainer}>
             <a href='https://www.aalto.fi/en/school-of-science' target='_blank' rel='noopener noreferrer'>
               <img className={classes.aaltoLogo} src={aaltoLogo} alt='logo' />
@@ -76,18 +70,13 @@ const Footer = props => {
             </a>
           </Grid>
         </Grid>
-      </Grid>
+      </div>
     </Paper>
   )
 }
 
 Footer.propTypes = {
-  /**
-   * Material-UI styles.
-   */
-  classes: PropTypes.object.isRequired
+  layoutConfig: PropTypes.object.isRequired
 }
 
-export const FooterComponent = Footer
-
-export default withStyles(styles)(Footer)
+export default Footer
diff --git a/src/client/components/perspectives/sampo/InstanceHomePage.js b/src/client/components/perspectives/sampo/InstanceHomePage.js
index 6d96e2b8..f01f8bdc 100644
--- a/src/client/components/perspectives/sampo/InstanceHomePage.js
+++ b/src/client/components/perspectives/sampo/InstanceHomePage.js
@@ -21,11 +21,12 @@ const styles = () => ({
     width: '100%',
     height: '100%'
   },
-  content: {
+  content: props => ({
+    padding: 0,
     width: '100%',
-    height: 'calc(100% - 72px)',
+    height: `calc(100% - ${props.layoutConfig.tabHeight}px)`,
     overflow: 'auto'
-  },
+  }),
   spinnerContainer: {
     display: 'flex',
     width: '100%',
@@ -151,7 +152,7 @@ class InstanceHomePage extends React.Component {
   }
 
   render = () => {
-    const { classes, perspectiveState, perspectiveConfig, rootUrl, screenSize } = this.props
+    const { classes, perspectiveState, perspectiveConfig, rootUrl, screenSize, layoutConfig } = this.props
     const { instanceTableData, fetching } = perspectiveState
     const resultClass = perspectiveConfig.id
     const hasTableData = this.hasTableData()
@@ -161,6 +162,7 @@ class InstanceHomePage extends React.Component {
           routeProps={this.props.routeProps}
           tabs={perspectiveConfig.instancePageTabs}
           screenSize={screenSize}
+          layoutConfig={layoutConfig}
         />
         <Paper square className={classes.content}>
           {fetching && !hasTableData &&
@@ -188,6 +190,7 @@ class InstanceHomePage extends React.Component {
                     data={instanceTableData}
                     properties={this.getVisibleRows(perspectiveState.properties)}
                     screenSize={screenSize}
+                    layoutConfig={layoutConfig}
                   />}
               />
               <Route
@@ -206,6 +209,7 @@ class InstanceHomePage extends React.Component {
                     optimize={1.2}
                     style={cytoscapeStyle}
                     layout={coseLayout}
+                    layoutConfig={layoutConfig}
                   />}
               />
               <Route
@@ -224,6 +228,7 @@ class InstanceHomePage extends React.Component {
                     style={cytoscapeStyle}
                     layout={coseLayout}
                     preprocess={preprocess}
+                    layoutConfig={layoutConfig}
                   />}
               />
               <Route
@@ -241,6 +246,7 @@ class InstanceHomePage extends React.Component {
                     xaxisTitle='Year'
                     yaxisTitle='Number of letters'
                     resultClass='emloSentReceived'
+                    layoutConfig={layoutConfig}
                   />}
               />
               <Route
@@ -261,6 +267,7 @@ class InstanceHomePage extends React.Component {
                     clearGeoJSONLayers={this.props.clearGeoJSONLayers}
                     fetchByURI={this.props.fetchByURI}
                     showError={this.props.showError}
+                    layoutConfig={layoutConfig}
                   />}
               />
               <Route
@@ -270,6 +277,7 @@ class InstanceHomePage extends React.Component {
                     sparqlQuery={this.props.sparqlQuery}
                     pageType='instancePage'
                     id={instanceTableData.id}
+                    layoutConfig={layoutConfig}
                   />}
               />
             </>}
@@ -359,7 +367,8 @@ InstanceHomePage.propTypes = {
   /**
     * Root url of the application.
     */
-  rootUrl: PropTypes.string.isRequired
+  rootUrl: PropTypes.string.isRequired,
+  layoutConfig: PropTypes.object.isRequired
 }
 
 export const InstanceHomePageComponent = InstanceHomePage
diff --git a/src/client/components/perspectives/sampo/Perspective1.js b/src/client/components/perspectives/sampo/Perspective1.js
index e110f258..f81bc0fb 100644
--- a/src/client/components/perspectives/sampo/Perspective1.js
+++ b/src/client/components/perspectives/sampo/Perspective1.js
@@ -31,6 +31,7 @@ const Perspective1 = props => {
         routeProps={props.routeProps}
         tabs={props.perspective.tabs}
         screenSize={props.screenSize}
+        layoutConfig={props.layoutConfig}
       />
       <Route
         exact path={`${rootUrl}/${perspective.id}/faceted-search`}
@@ -50,12 +51,15 @@ const Perspective1 = props => {
             sortResults={props.sortResults}
             routeProps={routeProps}
             rootUrl={rootUrl}
+            layoutConfig={props.layoutConfig}
           />}
       />
       <Route
         path={`${rootUrl}/${perspective.id}/faceted-search/production_places`}
         render={() =>
           <LeafletMap
+            mapBoxAccessToken={MAPBOX_ACCESS_TOKEN}
+            mapBoxStyle={MAPBOX_STYLE}
             center={props.perspectiveState.maps.placesMsProduced.center}
             zoom={props.perspectiveState.maps.placesMsProduced.zoom}
             // center={[60.187, 24.821]}
@@ -88,6 +92,7 @@ const Perspective1 = props => {
             // customMapControl
             layerConfigs={layerConfigs}
             infoHeaderExpanded={props.perspectiveState.facetedSearchHeaderExpanded}
+            layoutConfig={props.layoutConfig}
           // activeLayers={[
           // 'WFS_MV_KulttuuriymparistoSuojellut:Muinaisjaannokset_alue',
           // 'WFS_MV_KulttuuriymparistoSuojellut:Muinaisjaannokset_piste',
@@ -112,12 +117,15 @@ const Perspective1 = props => {
             mapBoxAccessToken={MAPBOX_ACCESS_TOKEN}
             mapBoxStyle={MAPBOX_STYLE}
             updateMapBounds={props.updateMapBounds}
+            layoutConfig={props.layoutConfig}
           />}
       />
       <Route
         path={`${rootUrl}/${perspective.id}/faceted-search/last_known_locations`}
         render={() =>
           <LeafletMap
+            mapBoxAccessToken={MAPBOX_ACCESS_TOKEN}
+            mapBoxStyle={MAPBOX_STYLE}
             center={props.perspectiveState.maps.lastKnownLocations.center}
             zoom={props.perspectiveState.maps.lastKnownLocations.zoom}
             results={props.perspectiveState.results}
@@ -147,6 +155,7 @@ const Perspective1 = props => {
             layerControlExpanded={layerControlExpanded}
             layerConfigs={layerConfigs}
             infoHeaderExpanded={props.perspectiveState.facetedSearchHeaderExpanded}
+            layoutConfig={props.layoutConfig}
           />}
       />
       <Route
@@ -180,6 +189,7 @@ const Perspective1 = props => {
             showTooltips
             mapBoxAccessToken={MAPBOX_ACCESS_TOKEN}
             mapBoxStyle={MAPBOX_STYLE}
+            layoutConfig={props.layoutConfig}
           />}
       />
       <Route
@@ -202,6 +212,7 @@ const Perspective1 = props => {
             stroke={{ width: 2 }}
             resultClass='productionTimespanLineChart'
             facetClass='perspective1'
+            layoutConfig={props.layoutConfig}
           />}
       />
       <Route
@@ -237,6 +248,7 @@ const Perspective1 = props => {
             }}
             resultClass='eventLineChart'
             facetClass='perspective1'
+            layoutConfig={props.layoutConfig}
           />}
       />
       <Route
@@ -256,6 +268,7 @@ const Perspective1 = props => {
             layout={coseLayout}
             preprocess={preprocess}
             pageType='facetResults'
+            layoutConfig={props.layoutConfig}
           />}
       />
       <Route
@@ -268,6 +281,7 @@ const Perspective1 = props => {
             pageType='facetResults'
             fetchPaginatedResults={props.fetchPaginatedResults}
             updatePage={props.updatePage}
+            layoutConfig={props.layoutConfig}
           />}
       />
     </>
@@ -362,7 +376,8 @@ Perspective1.propTypes = {
   /**
     * Root url of the application.
     */
-  rootUrl: PropTypes.string.isRequired
+  rootUrl: PropTypes.string.isRequired,
+  layoutConfig: PropTypes.object.isRequired
 }
 
 export default Perspective1
diff --git a/src/client/components/perspectives/sampo/Perspective2.js b/src/client/components/perspectives/sampo/Perspective2.js
index 119eaac1..a125b74f 100644
--- a/src/client/components/perspectives/sampo/Perspective2.js
+++ b/src/client/components/perspectives/sampo/Perspective2.js
@@ -13,6 +13,7 @@ const Perspective2 = props => {
         routeProps={props.routeProps}
         tabs={props.perspective.tabs}
         screenSize={props.screenSize}
+        layoutConfig={props.layoutConfig}
       />
       <Route
         exact path={`${rootUrl}/${perspective.id}/faceted-search`}
@@ -32,6 +33,7 @@ const Perspective2 = props => {
             sortResults={props.sortResults}
             routeProps={routeProps}
             rootUrl={rootUrl}
+            layoutConfig={props.layoutConfig}
           />}
       />
       <Route
@@ -44,6 +46,7 @@ const Perspective2 = props => {
             pageType='facetResults'
             fetchPaginatedResults={props.fetchPaginatedResults}
             updatePage={props.updatePage}
+            layoutConfig={props.layoutConfig}
           />}
       />
     </>
@@ -138,7 +141,8 @@ Perspective2.propTypes = {
   /**
    * Root url of the application.
    */
-  rootUrl: PropTypes.string.isRequired
+  rootUrl: PropTypes.string.isRequired,
+  layoutConfig: PropTypes.object.isRequired
 }
 
 export default Perspective2
diff --git a/src/client/components/perspectives/sampo/Perspective3.js b/src/client/components/perspectives/sampo/Perspective3.js
index 3fa7f134..5fb6d5b2 100644
--- a/src/client/components/perspectives/sampo/Perspective3.js
+++ b/src/client/components/perspectives/sampo/Perspective3.js
@@ -5,6 +5,10 @@ import PerspectiveTabs from '../../main_layout/PerspectiveTabs'
 import ResultTable from '../../facet_results/ResultTable'
 import Export from '../../facet_results/Export'
 import LeafletMap from '../../facet_results/LeafletMap'
+import {
+  MAPBOX_ACCESS_TOKEN,
+  MAPBOX_STYLE
+} from '../../../configs/sampo/GeneralConfig'
 import { layerConfigs, createPopUpContentMMM } from '../../../configs/sampo/Leaflet/LeafletConfig'
 
 const Perspective3 = props => {
@@ -37,12 +41,15 @@ const Perspective3 = props => {
             sortResults={props.sortResults}
             routeProps={routeProps}
             rootUrl={rootUrl}
+            layoutConfig={props.layoutConfig}
           />}
       />
       <Route
         path={`${rootUrl}/${perspective.id}/faceted-search/map`}
         render={() =>
           <LeafletMap
+            mapBoxAccessToken={MAPBOX_ACCESS_TOKEN}
+            mapBoxStyle={MAPBOX_STYLE}
             center={props.perspectiveState.maps.placesEvents.center}
             zoom={props.perspectiveState.maps.placesEvents.zoom}
             results={props.perspectiveState.results}
@@ -71,6 +78,7 @@ const Perspective3 = props => {
             layerControlExpanded={layerControlExpanded}
             layerConfigs={layerConfigs}
             infoHeaderExpanded={props.perspectiveState.facetedSearchHeaderExpanded}
+            layoutConfig={props.layoutConfig}
           />}
       />
       <Route
@@ -83,6 +91,7 @@ const Perspective3 = props => {
             pageType='facetResults'
             fetchPaginatedResults={props.fetchPaginatedResults}
             updatePage={props.updatePage}
+            layoutConfig={props.layoutConfig}
           />}
       />
     </>
@@ -177,7 +186,8 @@ Perspective3.propTypes = {
   /**
      * Root url of the application.
      */
-  rootUrl: PropTypes.string.isRequired
+  rootUrl: PropTypes.string.isRequired,
+  layoutConfig: PropTypes.object.isRequired
 }
 
 export default Perspective3
diff --git a/src/client/components/perspectives/sampo/TopBar.js b/src/client/components/perspectives/sampo/TopBar.js
index eac950ff..0015a85a 100644
--- a/src/client/components/perspectives/sampo/TopBar.js
+++ b/src/client/components/perspectives/sampo/TopBar.js
@@ -17,31 +17,34 @@ import TopBarLanguageButton from '../../main_layout/TopBarLanguageButton'
 import Divider from '@material-ui/core/Divider'
 import { has } from 'lodash'
 import secoLogo from '../../../img/logos/seco-logo-48x50.png'
-import { showLanguageButton, feedbackLink } from '../../../configs/sampo/GeneralConfig'
 
-const useStyles = makeStyles((theme) => ({
+const useStyles = makeStyles(theme => ({
   grow: {
     flexGrow: 1
   },
-  toolbar: {
+  topBarToolbar: props => ({
+    minHeight: props.layoutConfig.topBar.reducedHeight,
+    [theme.breakpoints.up(props.layoutConfig.reducedHeightBreakpoint)]: {
+      minHeight: props.layoutConfig.topBar.defaultHeight
+    },
     paddingLeft: theme.spacing(1.5),
     paddingRight: theme.spacing(1.5)
-  },
-  sectionDesktop: {
+  }),
+  sectionDesktop: props => ({
     display: 'none',
-    [theme.breakpoints.up('lg')]: {
+    [theme.breakpoints.up(props.layoutConfig.topBar.mobileMenuBreakpoint)]: {
       display: 'flex'
     }
-  },
+  }),
   link: {
     textDecoration: 'none'
   },
-  sectionMobile: {
+  sectionMobile: props => ({
     display: 'flex',
-    [theme.breakpoints.up('lg')]: {
+    [theme.breakpoints.up(props.layoutConfig.topBar.mobileMenuBreakpoint)]: {
       display: 'none'
     }
-  },
+  }),
   homeButtonText: {
     whiteSpace: 'nowrap',
     [theme.breakpoints.down('sm')]: {
@@ -61,12 +64,12 @@ const useStyles = makeStyles((theme) => ({
     marginRight: theme.spacing(1),
     borderLeft: '2px solid white'
   },
-  secoLogo: {
+  secoLogo: props => ({
     marginLeft: theme.spacing(1),
-    [theme.breakpoints.down('md')]: {
+    [theme.breakpoints.down(props.layoutConfig.topBar.mobileMenuBreakpoint)]: {
       display: 'none'
     }
-  }
+  })
 }))
 
 /**
@@ -77,7 +80,7 @@ const TopBar = props => {
   const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = React.useState(null)
   const isMobileMenuOpen = Boolean(mobileMoreAnchorEl)
   const { perspectives, currentLocale, availableLocales, rootUrl } = props
-  const classes = useStyles()
+  const classes = useStyles(props)
   const handleMobileMenuOpen = event => setMobileMoreAnchorEl(event.currentTarget)
   const handleMobileMenuClose = () => setMobileMoreAnchorEl(null)
   const clientFSMode = props.location.pathname.indexOf('clientFS') !== -1
@@ -166,7 +169,7 @@ const TopBar = props => {
       <Divider />
       {renderMobileMenuItem({
         id: 'feedback',
-        externalUrl: feedbackLink,
+        externalUrl: props.layoutConfig.topBar.feedbackLink,
         label: intl.get('topBar.feedback')
       })}
       {/* <MenuItem
@@ -212,8 +215,8 @@ const TopBar = props => {
       {/* Add an empty Typography element to ensure that that the MuiTypography class is loaded for
          any lower level components that use MuiTypography class only in translation files */}
       <Typography />
-      <AppBar position='absolute'>
-        <Toolbar className={classes.toolbar}>
+      <AppBar position='static'>
+        <Toolbar className={classes.topBarToolbar}>
           <Button component={AdapterLink} to='/'>
             <Typography className={classes.homeButtonText} variant='h6'>{intl.get('appTitle.short')}</Typography>
           </Button>
@@ -230,7 +233,7 @@ const TopBar = props => {
             <div className={classes.appBarDivider} />
             {renderDesktopTopMenuItem({
               id: 'feedback',
-              externalUrl: feedbackLink,
+              externalUrl: props.layoutConfig.topBar.feedbackLink,
               label: intl.get('topBar.feedback')
             })}
             <TopBarInfoButton rootUrl={props.rootUrl} />
@@ -243,7 +246,7 @@ const TopBar = props => {
             >
               {intl.get('topBar.instructions')}
             </Button>
-            {showLanguageButton &&
+            {props.layoutConfig.topBar.showLanguageButton &&
               <TopBarLanguageButton
                 currentLocale={currentLocale}
                 availableLocales={availableLocales}
@@ -260,7 +263,7 @@ const TopBar = props => {
             <Button><img src={secoLogo} /></Button>
           </a>
           <div className={classes.sectionMobile}>
-            {showLanguageButton &&
+            {props.layoutConfig.topBar.showLanguageButton &&
               <TopBarLanguageButton
                 currentLocale={currentLocale}
                 availableLocales={availableLocales}
@@ -314,7 +317,8 @@ TopBar.propTypes = {
   /**
    * Root url of the application.
    */
-  rootUrl: PropTypes.string.isRequired
+  rootUrl: PropTypes.string.isRequired,
+  layoutConfig: PropTypes.object.isRequired
 }
 
 export default TopBar
diff --git a/src/client/components/perspectives/sampo/client_fs/ClientFSMain.js b/src/client/components/perspectives/sampo/client_fs/ClientFSMain.js
index a79f7549..4a1c82cc 100644
--- a/src/client/components/perspectives/sampo/client_fs/ClientFSMain.js
+++ b/src/client/components/perspectives/sampo/client_fs/ClientFSMain.js
@@ -5,20 +5,12 @@ import Typography from '@material-ui/core/Typography'
 import Paper from '@material-ui/core/Paper'
 import intl from 'react-intl-universal'
 import bgImage from '../../../../img/main_page/bg2.jpg'
-import Footer from '../Footer'
 
 const styles = theme => ({
-  root: {
+  paper: {
     height: '100%',
-    // width: '100%',
     display: 'flex',
-    // alignItems: 'center',
     justifyContent: 'center',
-    // paddingTop: theme.spacing(3),
-    // paddingLeft: theme.spacing(3),
-    // paddingRight: theme.spacing(3),
-    // borderBottomLeftRadius: 0,
-    // borderBottomRightRadius: 0,
     backgroundImage: `url(${bgImage})`,
     backgroundPosition: 'center',
     backgroundRepeat: 'no-repeat',
@@ -48,30 +40,27 @@ const styles = theme => ({
 const ClientFSMain = props => {
   const { classes } = props
   return (
-    <>
-      <Paper className={classes.root}>
-        <div className={classes.content}>
-          <div className={classes.textContainer}>
-            <Typography className={classes.frontPageHeading} component='h1' variant='h3' align='center' color='textPrimary' gutterBottom>
-              {intl.getHTML('appTitle.long')}
-            </Typography>
-            <Typography className={classes.frontPageText} variant='h5' align='left' color='textPrimary' paragraph>
-              {intl.get('appDescription1')}
-            </Typography>
-            <Typography className={classes.frontPageText} variant='h5' align='left' color='textPrimary' paragraph>
-              {intl.get('appDescription2')}
-            </Typography>
-            <Typography className={classes.frontPageText} variant='h5' align='left' color='textPrimary' paragraph>
-              {intl.get('appDescription3')}
-            </Typography>
-            <Typography className={classes.frontPageText} variant='h5' align='left' color='textPrimary' paragraph>
-              {intl.get('appDescription4')}
-            </Typography>
-          </div>
+    <Paper className={classes.paper}>
+      <div className={classes.content}>
+        <div className={classes.textContainer}>
+          <Typography className={classes.frontPageHeading} component='h1' variant='h3' align='center' color='textPrimary' gutterBottom>
+            {intl.getHTML('appTitle.long')}
+          </Typography>
+          <Typography className={classes.frontPageText} variant='h5' align='left' color='textPrimary' paragraph>
+            {intl.get('appDescription1')}
+          </Typography>
+          <Typography className={classes.frontPageText} variant='h5' align='left' color='textPrimary' paragraph>
+            {intl.get('appDescription2')}
+          </Typography>
+          <Typography className={classes.frontPageText} variant='h5' align='left' color='textPrimary' paragraph>
+            {intl.get('appDescription3')}
+          </Typography>
+          <Typography className={classes.frontPageText} variant='h5' align='left' color='textPrimary' paragraph>
+            {intl.get('appDescription4')}
+          </Typography>
         </div>
-      </Paper>
-      <Footer />
-    </>
+      </div>
+    </Paper>
   )
 }
 
diff --git a/src/client/components/perspectives/sampo/client_fs/ClientFSPerspective.js b/src/client/components/perspectives/sampo/client_fs/ClientFSPerspective.js
index a381f1b0..6feea1e7 100644
--- a/src/client/components/perspectives/sampo/client_fs/ClientFSPerspective.js
+++ b/src/client/components/perspectives/sampo/client_fs/ClientFSPerspective.js
@@ -9,11 +9,14 @@ import ResultInfo from '../../../facet_results/ResultInfo'
 import VirtualizedTable from '../../../facet_results/VirtualizedTable'
 import Pie from '../../../facet_results/Pie.js'
 import CSVButton from '../../../facet_results/CSVButton'
-import Footer from '../Footer'
+import {
+  MAPBOX_ACCESS_TOKEN,
+  MAPBOX_STYLE
+} from '../../../../configs/sampo/GeneralConfig'
 import { createPopUpContentNameSampo, layerConfigs } from '../../../../configs/sampo/Leaflet/LeafletConfig'
 
 const ClientFSPerspective = props => {
-  const { rootUrl, perspective, screenSize, clientFSState } = props
+  const { rootUrl, perspective, screenSize, clientFSState, layoutConfig } = props
   const { maps } = clientFSState
   const { clientFSMapClusters, clientFSMapMarkers } = maps
   // console.log(clientFSMapClusters)
@@ -26,6 +29,7 @@ const ClientFSPerspective = props => {
         routeProps={props.routeProps}
         tabs={perspective.tabs}
         screenSize={props.screenSize}
+        layoutConfig={layoutConfig}
       />
       <Route
         exact path={`${rootUrl}/${perspective.id}/federated-search`}
@@ -39,12 +43,15 @@ const ClientFSPerspective = props => {
             clientFSState={props.clientFSState}
             clientFSSortResults={props.clientFSSortResults}
             perspectiveID={perspective.id}
+            layoutConfig={layoutConfig}
           />}
       />
       <Route
         path={`${rootUrl}/${perspective.id}/federated-search/map_clusters`}
         render={() =>
           <LeafletMap
+            mapBoxAccessToken={MAPBOX_ACCESS_TOKEN}
+            mapBoxStyle={MAPBOX_STYLE}
             center={clientFSMapClusters.center}
             zoom={clientFSMapClusters.zoom}
             results={props.clientFSResults}
@@ -65,6 +72,7 @@ const ClientFSPerspective = props => {
             layerControlExpanded={layerControlExpanded}
             layerConfigs={layerConfigs}
             updateMapBounds={props.updateMapBounds}
+            layoutConfig={layoutConfig}
           />}
       />
       <Route
@@ -75,6 +83,8 @@ const ClientFSPerspective = props => {
           } else {
             return (
               <LeafletMap
+                mapBoxAccessToken={MAPBOX_ACCESS_TOKEN}
+                mapBoxStyle={MAPBOX_STYLE}
                 center={clientFSMapMarkers.center}
                 zoom={clientFSMapMarkers.zoom}
                 results={props.clientFSResults}
@@ -95,6 +105,7 @@ const ClientFSPerspective = props => {
                 layerControlExpanded={layerControlExpanded}
                 layerConfigs={layerConfigs}
                 updateMapBounds={props.updateMapBounds}
+                layoutConfig={layoutConfig}
               />
             )
           }
@@ -105,17 +116,17 @@ const ClientFSPerspective = props => {
         render={() =>
           <Pie
             data={props.clientFSResults}
-            groupBy={props.clientFS.groupBy}
-            groupByLabel={props.clientFS.groupByLabel}
-            query={props.clientFS.query}
+            groupBy={props.clientFSState.groupBy}
+            groupByLabel={props.clientFSState.groupByLabel}
+            query={props.clientFSState.query}
+            layoutConfig={layoutConfig}
           />}
       />
       <Route
         path={`${rootUrl}/${perspective.id}/federated-search/download`}
         render={() =>
-          <CSVButton results={props.clientFSResults} />}
+          <CSVButton results={props.clientFSResults} layoutConfig={layoutConfig} />}
       />
-      <Footer />
     </>
   )
 }
diff --git a/src/client/configs/sampo/GeneralConfig.js b/src/client/configs/sampo/GeneralConfig.js
index d424c816..7c251412 100644
--- a/src/client/configs/sampo/GeneralConfig.js
+++ b/src/client/configs/sampo/GeneralConfig.js
@@ -1,9 +1,9 @@
+import bannerImage from '../../img/main_page/mmm-banner.jpg'
+
 export const rootUrl = ''
 
 export const defaultLocale = 'en'
 
-export const showLanguageButton = true
-
 export const readTranslationsFromGoogleSheets = false
 
 export const MAPBOX_ACCESS_TOKEN = 'pk.eyJ1IjoiZWtrb25lbiIsImEiOiJja2FkbGxiY2owMDZkMnFxcGVqNTZ0dmk2In0.6keLTN8VveJkM5y4_OFmUw' // https://docs.mapbox.com/accounts/overview/tokens/
@@ -28,4 +28,38 @@ export const SLIDER_DURATION = {
   doubleSpeed: 300
 }
 
-export const feedbackLink = 'https://link.webropolsurveys.com/'
+export const layoutConfig = {
+  hundredPercentHeightBreakPoint: 'md',
+  reducedHeightBreakpoint: 'lg',
+  tabHeight: 58,
+  paginationToolbarHeight: 37,
+  topBar: {
+    showLanguageButton: true,
+    feedbackLink: 'https://link.webropolsurveys.com/',
+    reducedHeight: 44,
+    defaultHeight: 64,
+    mobileMenuBreakpoint: 1360
+  },
+  mainPage: {
+    bannerBackround: `linear-gradient( rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0.45) ), url(${bannerImage})`,
+    bannerDefaultHeight: 300,
+    bannerReducedHeight: 220
+  },
+  infoHeader: {
+    default: {
+      height: 49,
+      expandedContentHeight: 160,
+      headingVariant: 'h4',
+      infoIconFontSize: 40
+    },
+    reducedHeight: {
+      height: 40,
+      expandedContentHeight: 100,
+      headingVariant: 'h6',
+      infoIconFontSize: 32
+    }
+  },
+  footer: {
+    height: 64
+  }
+}
diff --git a/src/client/configs/sampo/Leaflet/LeafletConfig.js b/src/client/configs/sampo/Leaflet/LeafletConfig.js
index 0e997a8e..6629d360 100644
--- a/src/client/configs/sampo/Leaflet/LeafletConfig.js
+++ b/src/client/configs/sampo/Leaflet/LeafletConfig.js
@@ -215,6 +215,9 @@ const createArchealogicalSiteColor = feature => {
 }
 
 /*
+  FHA spatial data general documentation:
+    https://www.museovirasto.fi/en/services-and-guidelines/data-systems/kulttuuriympaeristoen-tietojaerjestelmae/kulttuuriympaeristoen-paikkatietoaineistot
+
   FHA WFS services:
     https://kartta.nba.fi/arcgis/rest/services/WFS/MV_KulttuuriymparistoSuojellut/MapServer
     https://kartta.nba.fi/arcgis/rest/services/WFS/MV_Kulttuuriymparisto/MapServer/
diff --git a/src/client/configs/sampo/PerspectiveConfig.js b/src/client/configs/sampo/PerspectiveConfig.js
index 6b09b29c..f0aaf623 100644
--- a/src/client/configs/sampo/PerspectiveConfig.js
+++ b/src/client/configs/sampo/PerspectiveConfig.js
@@ -19,7 +19,6 @@ export const perspectiveConfig = [
   {
     id: 'perspective1',
     frontPageImage: manuscriptsImage,
-    perspectiveDescHeight: 160,
     defaultActiveFacets: new Set([]),
     tabs: [
       {
@@ -89,7 +88,6 @@ export const perspectiveConfig = [
   {
     id: 'perspective2',
     frontPageImage: worksImage,
-    perspectiveDescHeight: 160,
     defaultActiveFacets: new Set(['prefLabel']),
     tabs: [
       {
@@ -119,7 +117,6 @@ export const perspectiveConfig = [
   {
     id: 'perspective3',
     frontPageImage: eventsImage,
-    perspectiveDescHeight: 160,
     defaultActiveFacets: new Set(['prefLabel']),
     tabs: [
       {
@@ -157,7 +154,6 @@ export const perspectiveConfig = [
     id: 'finds',
     isHidden: true,
     frontPageImage: null,
-    perspectiveDescHeight: 160,
     defaultActiveFacets: new Set(['prefLabel']),
     tabs: [
       {
@@ -198,7 +194,7 @@ export const perspectiveConfig = [
     id: 'emloActors',
     isHidden: true,
     frontPageImage: null,
-    perspectiveDescHeight: 160,
+
     defaultActiveFacets: new Set(['prefLabel']),
     tabs: [
       {
@@ -244,7 +240,6 @@ export const perspectiveConfig = [
     id: 'emloLetters',
     isHidden: true,
     frontPageImage: null,
-    perspectiveDescHeight: 160,
     defaultActiveFacets: new Set(['prefLabel']),
     tabs: [
       {
@@ -285,7 +280,6 @@ export const perspectiveConfig = [
     id: 'emloPlaces',
     isHidden: true,
     frontPageImage: null,
-    perspectiveDescHeight: 160,
     defaultActiveFacets: new Set(['prefLabel']),
     tabs: [
       {
@@ -316,7 +310,6 @@ export const perspectiveConfig = [
     id: 'hellerau',
     isHidden: true,
     frontPageImage: null,
-    perspectiveDescHeight: 160,
     defaultActiveFacets: new Set(['prefLabel']),
     tabs: [
       {
diff --git a/src/client/containers/SemanticPortal.js b/src/client/containers/SemanticPortal.js
index 85296f25..bfe4f6fb 100644
--- a/src/client/containers/SemanticPortal.js
+++ b/src/client/containers/SemanticPortal.js
@@ -33,7 +33,7 @@ import Footer from '../components/perspectives/sampo/Footer'
 import KnowledgeGraphMetadataTable from '../components/perspectives/sampo/KnowledgeGraphMetadataTable'
 import { perspectiveConfig } from '../configs/sampo/PerspectiveConfig'
 import { perspectiveConfigOnlyInfoPages } from '../configs/sampo/PerspectiveConfigOnlyInfoPages'
-import { rootUrl } from '../configs/sampo/GeneralConfig'
+import { rootUrl, layoutConfig } from '../configs/sampo/GeneralConfig'
 // ** Portal specific components and configs end **
 
 import {
@@ -73,93 +73,66 @@ import { filterResults } from '../selectors'
 
 const useStyles = makeStyles(theme => ({
   root: {
-    flexGrow: 1,
-    // Set app height for different screen sizes
-    height: 'auto',
-    [theme.breakpoints.up('md')]: {
-      height: '100%'
-    },
     /* Background color of the app.
        In order to use both 'auto' and '100%' heights, bg-color
        needs to be defined also in index.html (for #app and #root elements)
     */
-    backgroundColor: '#bdbdbd'
-  },
-  flex: {
-    flexGrow: 1
-  },
-  appFrame: {
-    height: '100%',
-    zIndex: 1,
-    overflow: 'hidden',
-    position: 'relative',
-    display: 'flex',
-    width: '100%'
-  },
-  mainContainer: {
-    height: 'auto',
-    overflow: 'auto',
-    [theme.breakpoints.up('md')]: {
-      height: 'calc(100% - 64px)' // 100% - app bar - padding
-    },
-    [theme.breakpoints.down('sm')]: {
-      marginTop: 56 // app bar
-    },
-    [theme.breakpoints.up('sm')]: {
-      marginTop: 64 // app bar
+    backgroundColor: '#bdbdbd',
+    [theme.breakpoints.up(layoutConfig.hundredPercentHeightBreakPoint)]: {
+      overflow: 'hidden',
+      height: '100%'
     }
   },
   mainContainerClientFS: {
-    height: 'auto',
-    [theme.breakpoints.up('md')]: {
-      height: 'calc(100% - 144px)' // 100% - app bar - padding * 2
-    },
-    [theme.breakpoints.down('sm')]: {
-      marginTop: 64 // app bar
+    marginTop: theme.spacing(1),
+    marginBottom: theme.spacing(3),
+    [theme.breakpoints.up(layoutConfig.hundredPercentHeightBreakPoint)]: {
+      height: `calc(100% - ${layoutConfig.topBar.reducedHeight + layoutConfig.footer.height + theme.spacing(0.5)}px)`,
+      marginBottom: theme.spacing(1)
     },
-    [theme.breakpoints.up('sm')]: {
-      marginTop: 72 // app bar + padding
+    [theme.breakpoints.up(layoutConfig.reducedHeightBreakpoint)]: {
+      height: `calc(100% - ${layoutConfig.topBar.defaultHeight + layoutConfig.footer.height + theme.spacing(0.5)}px)`,
+      marginBottom: theme.spacing(1)
     }
   },
   textPageContainer: {
-    width: '100%',
-    paddingBottom: theme.spacing(1),
-    paddingLeft: theme.spacing(1),
-    paddingRight: theme.spacing(1),
-    [theme.breakpoints.up('md')]: {
-      height: 'calc(100% - 80px)'
-    },
-    [theme.breakpoints.down('sm')]: {
-      marginTop: 64 // app bar + padding
+    margin: theme.spacing(0.5),
+    width: `calc(100% - ${theme.spacing(1)}px)`,
+    [theme.breakpoints.up(layoutConfig.hundredPercentHeightBreakPoint)]: {
+      height: `calc(100% - ${layoutConfig.topBar.reducedHeight + theme.spacing(1.5)}px)`
     },
-    [theme.breakpoints.up('sm')]: {
-      marginTop: 72 // app bar + padding
+    [theme.breakpoints.up(layoutConfig.reducedHeightBreakpoint)]: {
+      height: `calc(100% - ${layoutConfig.topBar.defaultHeight + theme.spacing(1.5)}px)`
     }
   },
   perspectiveContainer: {
-    height: 'auto',
-    [theme.breakpoints.up('md')]: {
-      height: 'calc(100% - 130px)'
+    margin: theme.spacing(0.5),
+    width: `calc(100% - ${theme.spacing(1)}px)`,
+    [theme.breakpoints.up(layoutConfig.hundredPercentHeightBreakPoint)]: {
+      height: `calc(100% - ${layoutConfig.topBar.reducedHeight + layoutConfig.infoHeader.reducedHeight.height + theme.spacing(1.5)}px)`
     },
-    padding: theme.spacing(1),
-    [theme.breakpoints.down('sm')]: {
-      marginTop: 126 // app bar + header
-    },
-    [theme.breakpoints.up('sm')]: {
-      marginTop: 130 // app bar + header
+    [theme.breakpoints.up(layoutConfig.reducedHeightBreakpoint)]: {
+      height: `calc(100% - ${layoutConfig.topBar.defaultHeight + layoutConfig.infoHeader.default.height + theme.spacing(1.5)}px)`
     }
   },
   perspectiveContainerHeaderExpanded: {
-    height: 'auto',
-    [theme.breakpoints.up('md')]: {
-      height: 'calc(100% - 316px)'
+    margin: theme.spacing(0.5),
+    width: `calc(100% - ${theme.spacing(1)}px)`,
+    [theme.breakpoints.up(layoutConfig.hundredPercentHeightBreakPoint)]: {
+      height: `calc(100% - ${
+        layoutConfig.topBar.reducedHeight +
+        layoutConfig.infoHeader.reducedHeight.height +
+        layoutConfig.infoHeader.reducedHeight.expandedContentHeight +
+        theme.spacing(3.5)
+      }px)`
     },
-    padding: theme.spacing(1),
-    [theme.breakpoints.down('sm')]: {
-      marginTop: 308 // app bar + header
-    },
-    [theme.breakpoints.up('sm')]: {
-      marginTop: 316 // app bar + header
+    [theme.breakpoints.up(layoutConfig.reducedHeightBreakpoint)]: {
+      height: `calc(100% - ${
+        layoutConfig.topBar.defaultHeight +
+        layoutConfig.infoHeader.default.height +
+        layoutConfig.infoHeader.default.expandedContentHeight +
+        theme.spacing(3.5)
+      }px)`
     }
   },
   // perspective container is divided into two columns:
@@ -168,22 +141,22 @@ const useStyles = makeStyles(theme => ({
     [theme.breakpoints.up('md')]: {
       height: '100%'
     },
+    [theme.breakpoints.down('sm')]: {
+      paddingRight: '0px !important'
+    },
     overflow: 'auto',
+    paddingLeft: '0px !important',
     paddingTop: '0px !important',
     paddingBottom: '0px !important'
   },
   facetBarContainerClientFS: {
-    height: 'auto',
-    width: '100%',
-    [theme.breakpoints.up('md')]: {
-      height: '100%',
-      overflow: 'auto'
-    },
-    [theme.breakpoints.down('md')]: {
-      marginBottom: theme.spacing(1)
-    },
+    overflow: 'auto',
     paddingLeft: theme.spacing(0.5),
-    paddingRight: theme.spacing(0.5)
+    paddingRight: theme.spacing(0.5),
+    [theme.breakpoints.up(layoutConfig.hundredPercentHeightBreakPoint)]: {
+      minHeight: 'initial',
+      height: `calc(100% - ${theme.spacing(2)}px)`
+    }
   },
   resultsContainer: {
     height: 'auto',
@@ -192,60 +165,58 @@ const useStyles = makeStyles(theme => ({
     },
     paddingTop: '0px !important',
     paddingBottom: '0px !important',
+    paddingRight: '0px !important',
     [theme.breakpoints.down('sm')]: {
-      marginTop: theme.spacing(1)
+      paddingLeft: '0px !important',
+      marginBottom: theme.spacing(1),
+      marginTop: theme.spacing(0.5)
     }
   },
   resultsContainerClientFS: {
-    height: 800,
-    [theme.breakpoints.down('md')]: {
-      marginBottom: 8,
-      width: 'calc(100% - 2px)'
-    },
-    [theme.breakpoints.up('md')]: {
-      height: '100%'
+    minHeight: 500,
+    [theme.breakpoints.up(layoutConfig.hundredPercentHeightBreakPoint)]: {
+      minHeight: 'initial',
+      height: `calc(100% - ${theme.spacing(2)}px)`
     },
-    paddingTop: '0px !important',
     paddingBottom: '0px !important',
     paddingRight: theme.spacing(0.5),
     paddingLeft: theme.spacing(0.5)
-    // [theme.breakpoints.down('sm')]: {
-    //   marginTop: theme.spacing(1)
-    // }
   },
   instancePageContainer: {
-    height: 'auto',
-    [theme.breakpoints.up('md')]: {
-      height: 'calc(100% - 170px)'
-    },
-    padding: theme.spacing(1),
-    [theme.breakpoints.down('sm')]: {
-      marginTop: 164
+    margin: theme.spacing(0.5),
+    width: `calc(100% - ${theme.spacing(1)}px)`,
+    [theme.breakpoints.up(layoutConfig.hundredPercentHeightBreakPoint)]: {
+      height: `calc(100% - ${layoutConfig.topBar.reducedHeight + 2 * layoutConfig.infoHeader.reducedHeight.height + theme.spacing(1.5)}px)`
     },
-    [theme.breakpoints.up('sm')]: {
-      marginTop: 170
+    [theme.breakpoints.up(layoutConfig.reducedHeightBreakpoint)]: {
+      height: `calc(100% - ${layoutConfig.topBar.defaultHeight + 89 + theme.spacing(1.5)}px)`
     }
   },
   instancePageContainerHeaderExpanded: {
-    height: 'auto',
-    [theme.breakpoints.up('md')]: {
-      height: 'calc(100% - 354px)'
+    margin: theme.spacing(0.5),
+    width: `calc(100% - ${theme.spacing(1)}px)`,
+    [theme.breakpoints.up(layoutConfig.hundredPercentHeightBreakPoint)]: {
+      height: `calc(100% - ${
+        layoutConfig.topBar.reducedHeight +
+        2 * layoutConfig.infoHeader.reducedHeight.height +
+        layoutConfig.infoHeader.reducedHeight.expandedContentHeight +
+        theme.spacing(3.5)
+      }px)`
     },
-    padding: theme.spacing(1),
-    [theme.breakpoints.down('sm')]: {
-      marginTop: 348
-    },
-    [theme.breakpoints.up('sm')]: {
-      marginTop: 354
+    [theme.breakpoints.up(layoutConfig.reducedHeightBreakpoint)]: {
+      height: `calc(100% - ${
+        layoutConfig.topBar.defaultHeight +
+        89 +
+        layoutConfig.infoHeader.default.expandedContentHeight +
+        theme.spacing(3.5)
+      }px)`
     }
   },
   instancePageContent: {
-    height: 'auto',
-    [theme.breakpoints.up('md')]: {
+    [theme.breakpoints.up(layoutConfig.hundredPercentHeightBreakPoint)]: {
       height: '100%'
     },
-    paddingTop: '0px !important',
-    paddingBottom: '0px !important'
+    padding: '0px !important'
   }
 }))
 
@@ -280,118 +251,178 @@ const SemanticPortal = props => {
   return (
     <MuiPickersUtilsProvider libInstance={moment} utils={MomentUtils} locale={props.options.currentLocale}>
       <div className={classes.root}>
-        <div className={classes.appFrame}>
-          <Message error={error} />
-          <>
-            <TopBar
-              rootUrl={rootUrlWithLang}
-              search={props.fullTextSearch}
-              fetchFullTextResults={props.fetchFullTextResults}
-              clearResults={props.clearResults}
-              perspectives={perspectiveConfig}
-              currentLocale={props.options.currentLocale}
-              availableLocales={props.options.availableLocales}
-              loadLocales={props.loadLocales}
-              xsScreen={xsScreen}
-              location={props.location}
-            />
-            <Route exact path={`${rootUrl}/`}>
-              <Redirect to={rootUrlWithLang} />
-            </Route>
-            <Route
-              exact path={`${rootUrlWithLang}/`}
-              render={() =>
-                <Grid container spacing={1} className={classes.mainContainer}>
-                  <Main
-                    perspectives={perspectiveConfig}
+        <Message error={error} />
+        <>
+          <TopBar
+            rootUrl={rootUrlWithLang}
+            search={props.fullTextSearch}
+            fetchFullTextResults={props.fetchFullTextResults}
+            clearResults={props.clearResults}
+            perspectives={perspectiveConfig}
+            currentLocale={props.options.currentLocale}
+            availableLocales={props.options.availableLocales}
+            loadLocales={props.loadLocales}
+            xsScreen={xsScreen}
+            location={props.location}
+            layoutConfig={layoutConfig}
+          />
+          <Route exact path={`${rootUrl}/`}>
+            <Redirect to={rootUrlWithLang} />
+          </Route>
+          <Route
+            exact path={`${rootUrlWithLang}/`}
+            render={() =>
+              <>
+                <Main
+                  perspectives={perspectiveConfig}
+                  screenSize={screenSize}
+                  rootUrl={rootUrlWithLang}
+                  layoutConfig={layoutConfig}
+                />
+                <Footer layoutConfig={layoutConfig} />
+              </>}
+          />
+          {/* https://stackoverflow.com/a/41024944 */}
+          <Route
+            path={`${rootUrlWithLang}/`} render={({ location }) => {
+              if (typeof window.ga === 'function') {
+                window.ga('set', 'page', location.pathname + location.search)
+                window.ga('send', 'pageview')
+              }
+              return null
+            }}
+          />
+          {/* route for full text search results */}
+          <Route
+            path={`${rootUrlWithLang}/full-text-search`}
+            render={routeProps =>
+              <Grid container spacing={1} className={classes.mainContainer}>
+                <Grid item xs={12} className={classes.resultsContainer}>
+                  <FullTextSearch
+                    fullTextSearch={props.fullTextSearch}
+                    sortFullTextResults={props.sortFullTextResults}
+                    routeProps={routeProps}
                     screenSize={screenSize}
                     rootUrl={rootUrlWithLang}
+                    layoutConfig={layoutConfig}
                   />
-                  <Footer />
-                </Grid>}
-            />
-            {/* https://stackoverflow.com/a/41024944 */}
-            <Route
-              path={`${rootUrlWithLang}/`} render={({ location }) => {
-                if (typeof window.ga === 'function') {
-                  window.ga('set', 'page', location.pathname + location.search)
-                  window.ga('send', 'pageview')
-                }
-                return null
-              }}
-            />
-            {/* route for full text search results */}
-            <Route
-              path={`${rootUrlWithLang}/full-text-search`}
-              render={routeProps =>
-                <Grid container spacing={1} className={classes.mainContainer}>
-                  <Grid item xs={12} className={classes.resultsContainer}>
-                    <FullTextSearch
-                      fullTextSearch={props.fullTextSearch}
-                      sortFullTextResults={props.sortFullTextResults}
-                      routeProps={routeProps}
-                      screenSize={screenSize}
-                      rootUrl={rootUrlWithLang}
+                </Grid>
+              </Grid>}
+          />
+          {/* routes for faceted search perspectives */}
+          {perspectiveConfig.map(perspective => {
+            if (!has(perspective, 'externalUrl') && perspective.id !== 'placesClientFS') {
+              return (
+                <React.Fragment key={perspective.id}>
+                  <Route
+                    path={`${rootUrlWithLang}/${perspective.id}/faceted-search`}
+                    render={routeProps => {
+                      return (
+                        <>
+                          <InfoHeader
+                            resultClass={perspective.id}
+                            pageType='facetResults'
+                            expanded={props[perspective.id].facetedSearchHeaderExpanded}
+                            updateExpanded={props.updatePerspectiveHeaderExpanded}
+                            screenSize={screenSize}
+                            layoutConfig={layoutConfig}
+                          />
+                          <Grid
+                            container spacing={1} className={props[perspective.id].facetedSearchHeaderExpanded
+                              ? classes.perspectiveContainerHeaderExpanded
+                              : classes.perspectiveContainer}
+                          >
+                            <Grid item xs={12} md={3} className={classes.facetBarContainer}>
+                              <FacetBar
+                                facetedSearchMode='serverFS'
+                                facetData={props[`${perspective.id}Facets`]}
+                                facetDataConstrainSelf={has(props, `${perspective.id}FacetsConstrainSelf`)
+                                  ? props[`${perspective.id}FacetsConstrainSelf`]
+                                  : null}
+                                facetResults={props[`${perspective.id}`]}
+                                facetClass={perspective.id}
+                                resultClass={perspective.id}
+                                fetchingResultCount={props[perspective.id].fetchingResultCount}
+                                resultCount={props[perspective.id].resultCount}
+                                fetchFacet={props.fetchFacet}
+                                fetchFacetConstrainSelf={props.fetchFacetConstrainSelf}
+                                fetchResults={props.fetchResults}
+                                clearFacet={props.clearFacet}
+                                clearAllFacets={props.clearAllFacets}
+                                fetchResultCount={props.fetchResultCount}
+                                updateFacetOption={props.updateFacetOption}
+                                showError={props.showError}
+                                defaultActiveFacets={perspective.defaultActiveFacets}
+                                rootUrl={rootUrlWithLang}
+                                screenSize={screenSize}
+                                layoutConfig={layoutConfig}
+                              />
+                            </Grid>
+                            <Grid item xs={12} md={9} className={classes.resultsContainer}>
+                              <FacetedSearchPerspective
+                                perspectiveState={props[`${perspective.id}`]}
+                                perspectiveConfig={perspective}
+                                facetState={props[`${perspective.id}Facets`]}
+                                facetConstrainSelfState={has(props, `${perspective.id}FacetsConstrainSelf`)
+                                  ? props[`${perspective.id}FacetsConstrainSelf`]
+                                  : null}
+                                leafletMapState={props.leafletMap}
+                                fetchPaginatedResults={props.fetchPaginatedResults}
+                                fetchResults={props.fetchResults}
+                                fetchInstanceAnalysis={props.fetchInstanceAnalysis}
+                                fetchFacetConstrainSelf={props.fetchFacetConstrainSelf}
+                                fetchGeoJSONLayers={props.fetchGeoJSONLayers}
+                                fetchGeoJSONLayersBackend={props.fetchGeoJSONLayersBackend}
+                                clearGeoJSONLayers={props.clearGeoJSONLayers}
+                                fetchByURI={props.fetchByURI}
+                                updatePage={props.updatePage}
+                                updateRowsPerPage={props.updateRowsPerPage}
+                                updateFacetOption={props.updateFacetOption}
+                                updateMapBounds={props.updateMapBounds}
+                                sortResults={props.sortResults}
+                                showError={props.showError}
+                                routeProps={routeProps}
+                                perspective={perspective}
+                                animationValue={props.animationValue}
+                                animateMap={props.animateMap}
+                                screenSize={screenSize}
+                                rootUrl={rootUrlWithLang}
+                                layoutConfig={layoutConfig}
+                              />
+                            </Grid>
+                          </Grid>
+                        </>
+                      )
+                    }}
+                  />
+                  <Switch>
+                    <Redirect
+                      from={`/${perspective.id}/page/:id`}
+                      to={`${rootUrlWithLang}/${perspective.id}/page/:id`}
                     />
-                  </Grid>
-                </Grid>}
-            />
-            {/* routes for faceted search perspectives */}
-            {perspectiveConfig.map(perspective => {
-              if (!has(perspective, 'externalUrl') && perspective.id !== 'placesClientFS') {
-                return (
-                  <React.Fragment key={perspective.id}>
                     <Route
-                      path={`${rootUrlWithLang}/${perspective.id}/faceted-search`}
+                      path={`${rootUrlWithLang}/${perspective.id}/page/:id`}
                       render={routeProps => {
                         return (
                           <>
                             <InfoHeader
                               resultClass={perspective.id}
-                              pageType='facetResults'
-                              expanded={props[perspective.id].facetedSearchHeaderExpanded}
+                              pageType='instancePage'
+                              instanceData={props[perspective.id].instanceTableData}
+                              expanded={props[perspective.id].instancePageHeaderExpanded}
                               updateExpanded={props.updatePerspectiveHeaderExpanded}
-                              descriptionHeight={perspective.perspectiveDescHeight}
                               screenSize={screenSize}
+                              layoutConfig={layoutConfig}
                             />
                             <Grid
-                              container spacing={1} className={props[perspective.id].facetedSearchHeaderExpanded
-                                ? classes.perspectiveContainerHeaderExpanded
-                                : classes.perspectiveContainer}
+                              container spacing={1} className={props[perspective.id].instancePageHeaderExpanded
+                                ? classes.instancePageContainerHeaderExpanded
+                                : classes.instancePageContainer}
                             >
-                              <Grid item xs={12} md={3} className={classes.facetBarContainer}>
-                                <FacetBar
-                                  facetedSearchMode='serverFS'
-                                  facetData={props[`${perspective.id}Facets`]}
-                                  facetDataConstrainSelf={has(props, `${perspective.id}FacetsConstrainSelf`)
-                                    ? props[`${perspective.id}FacetsConstrainSelf`]
-                                    : null}
-                                  facetResults={props[`${perspective.id}`]}
-                                  facetClass={perspective.id}
-                                  resultClass={perspective.id}
-                                  fetchingResultCount={props[perspective.id].fetchingResultCount}
-                                  resultCount={props[perspective.id].resultCount}
-                                  fetchFacet={props.fetchFacet}
-                                  fetchFacetConstrainSelf={props.fetchFacetConstrainSelf}
-                                  fetchResults={props.fetchResults}
-                                  clearFacet={props.clearFacet}
-                                  clearAllFacets={props.clearAllFacets}
-                                  fetchResultCount={props.fetchResultCount}
-                                  updateFacetOption={props.updateFacetOption}
-                                  showError={props.showError}
-                                  defaultActiveFacets={perspective.defaultActiveFacets}
-                                  rootUrl={rootUrlWithLang}
-                                  screenSize={screenSize}
-                                />
-                              </Grid>
-                              <Grid item xs={12} md={9} className={classes.resultsContainer}>
-                                <FacetedSearchPerspective
+                              <Grid item xs={12} className={classes.instancePageContent}>
+                                <InstanceHomePage
                                   perspectiveState={props[`${perspective.id}`]}
                                   perspectiveConfig={perspective}
-                                  facetState={props[`${perspective.id}Facets`]}
-                                  facetConstrainSelfState={has(props, `${perspective.id}FacetsConstrainSelf`)
-                                    ? props[`${perspective.id}FacetsConstrainSelf`]
-                                    : null}
                                   leafletMapState={props.leafletMap}
                                   fetchPaginatedResults={props.fetchPaginatedResults}
                                   fetchResults={props.fetchResults}
@@ -413,6 +444,7 @@ const SemanticPortal = props => {
                                   animateMap={props.animateMap}
                                   screenSize={screenSize}
                                   rootUrl={rootUrlWithLang}
+                                  layoutConfig={layoutConfig}
                                 />
                               </Grid>
                             </Grid>
@@ -420,130 +452,76 @@ const SemanticPortal = props => {
                         )
                       }}
                     />
-                    <Switch>
-                      <Redirect
-                        from={`/${perspective.id}/page/:id`}
-                        to={`${rootUrlWithLang}/${perspective.id}/page/:id`}
-                      />
-                      <Route
-                        path={`${rootUrlWithLang}/${perspective.id}/page/:id`}
-                        render={routeProps => {
-                          return (
-                            <>
-                              <InfoHeader
-                                resultClass={perspective.id}
-                                pageType='instancePage'
-                                instanceData={props[perspective.id].instanceTableData}
-                                expanded={props[perspective.id].instancePageHeaderExpanded}
-                                updateExpanded={props.updatePerspectiveHeaderExpanded}
-                                descriptionHeight={perspective.perspectiveDescHeight}
-                                screenSize={screenSize}
-                              />
-                              <Grid
-                                container spacing={1} className={props[perspective.id].instancePageHeaderExpanded
-                                  ? classes.instancePageContainerHeaderExpanded
-                                  : classes.instancePageContainer}
-                              >
-                                <Grid item xs={12} className={classes.instancePageContent}>
-                                  <InstanceHomePage
-                                    perspectiveState={props[`${perspective.id}`]}
-                                    perspectiveConfig={perspective}
-                                    leafletMapState={props.leafletMap}
-                                    fetchPaginatedResults={props.fetchPaginatedResults}
-                                    fetchResults={props.fetchResults}
-                                    fetchInstanceAnalysis={props.fetchInstanceAnalysis}
-                                    fetchFacetConstrainSelf={props.fetchFacetConstrainSelf}
-                                    fetchGeoJSONLayers={props.fetchGeoJSONLayers}
-                                    fetchGeoJSONLayersBackend={props.fetchGeoJSONLayersBackend}
-                                    clearGeoJSONLayers={props.clearGeoJSONLayers}
-                                    fetchByURI={props.fetchByURI}
-                                    updatePage={props.updatePage}
-                                    updateRowsPerPage={props.updateRowsPerPage}
-                                    updateFacetOption={props.updateFacetOption}
-                                    updateMapBounds={props.updateMapBounds}
-                                    sortResults={props.sortResults}
-                                    showError={props.showError}
-                                    routeProps={routeProps}
-                                    perspective={perspective}
-                                    animationValue={props.animationValue}
-                                    animateMap={props.animateMap}
-                                    screenSize={screenSize}
-                                    rootUrl={rootUrlWithLang}
-                                  />
-                                </Grid>
-                              </Grid>
-                            </>
-                          )
-                        }}
+                  </Switch>
+                </React.Fragment>
+              )
+            }
+          })}
+          {/* create routes for classes that have only info pages and no faceted search perspective */}
+          {perspectiveConfigOnlyInfoPages.map(perspective =>
+            <Switch key={perspective.id}>
+              <Redirect
+                from={`${rootUrl}/${perspective.id}/page/:id`}
+                to={`${rootUrlWithLang}/${perspective.id}/page/:id`}
+              />
+              <Route
+                path={`${rootUrlWithLang}/${perspective.id}/page/:id`}
+                render={routeProps => {
+                  return (
+                    <>
+                      <InfoHeader
+                        resultClass={perspective.id}
+                        pageType='instancePage'
+                        instanceData={props[perspective.id].instanceTableData}
+                        expanded={props[perspective.id].instancePageHeaderExpanded}
+                        updateExpanded={props.updatePerspectiveHeaderExpanded}
+                        screenSize={screenSize}
+                        layoutConfig={layoutConfig}
                       />
-                    </Switch>
-                  </React.Fragment>
-                )
-              }
-            })}
-            {/* create routes for classes that have only info pages and no faceted search perspective */}
-            {perspectiveConfigOnlyInfoPages.map(perspective =>
-              <Switch key={perspective.id}>
-                <Redirect
-                  from={`${rootUrl}/${perspective.id}/page/:id`}
-                  to={`${rootUrlWithLang}/${perspective.id}/page/:id`}
-                />
-                <Route
-                  path={`${rootUrlWithLang}/${perspective.id}/page/:id`}
-                  render={routeProps => {
-                    return (
-                      <>
-                        <InfoHeader
-                          resultClass={perspective.id}
-                          pageType='instancePage'
-                          instanceData={props[perspective.id].instanceTableData}
-                          expanded={props[perspective.id].instancePageHeaderExpanded}
-                          updateExpanded={props.updatePerspectiveHeaderExpanded}
-                          descriptionHeight={perspective.perspectiveDescHeight}
-                          screenSize={screenSize}
-                        />
-                        <Grid
-                          container spacing={1} className={props[perspective.id].instancePageHeaderExpanded
-                            ? classes.instancePageContainerHeaderExpanded
-                            : classes.instancePageContainer}
-                        >
-                          <Grid item xs={12} className={classes.instancePageContent}>
-                            <InstanceHomePage
-                              perspectiveState={props[`${perspective.id}`]}
-                              perspectiveConfig={perspective}
-                              leafletMapState={props.leafletMap}
-                              fetchPaginatedResults={props.fetchPaginatedResults}
-                              fetchResults={props.fetchResults}
-                              fetchInstanceAnalysis={props.fetchInstanceAnalysis}
-                              fetchFacetConstrainSelf={props.fetchFacetConstrainSelf}
-                              fetchGeoJSONLayers={props.fetchGeoJSONLayers}
-                              fetchGeoJSONLayersBackend={props.fetchGeoJSONLayersBackend}
-                              clearGeoJSONLayers={props.clearGeoJSONLayers}
-                              fetchByURI={props.fetchByURI}
-                              updatePage={props.updatePage}
-                              updateRowsPerPage={props.updateRowsPerPage}
-                              updateFacetOption={props.updateFacetOption}
-                              updateMapBounds={props.updateMapBounds}
-                              sortResults={props.sortResults}
-                              showError={props.showError}
-                              routeProps={routeProps}
-                              perspective={perspective}
-                              animationValue={props.animationValue}
-                              animateMap={props.animateMap}
-                              screenSize={screenSize}
-                              rootUrl={rootUrlWithLang}
-                            />
-                          </Grid>
+                      <Grid
+                        container spacing={1} className={props[perspective.id].instancePageHeaderExpanded
+                          ? classes.instancePageContainerHeaderExpanded
+                          : classes.instancePageContainer}
+                      >
+                        <Grid item xs={12} className={classes.instancePageContent}>
+                          <InstanceHomePage
+                            perspectiveState={props[`${perspective.id}`]}
+                            perspectiveConfig={perspective}
+                            leafletMapState={props.leafletMap}
+                            fetchPaginatedResults={props.fetchPaginatedResults}
+                            fetchResults={props.fetchResults}
+                            fetchInstanceAnalysis={props.fetchInstanceAnalysis}
+                            fetchFacetConstrainSelf={props.fetchFacetConstrainSelf}
+                            fetchGeoJSONLayers={props.fetchGeoJSONLayers}
+                            fetchGeoJSONLayersBackend={props.fetchGeoJSONLayersBackend}
+                            clearGeoJSONLayers={props.clearGeoJSONLayers}
+                            fetchByURI={props.fetchByURI}
+                            updatePage={props.updatePage}
+                            updateRowsPerPage={props.updateRowsPerPage}
+                            updateFacetOption={props.updateFacetOption}
+                            updateMapBounds={props.updateMapBounds}
+                            sortResults={props.sortResults}
+                            showError={props.showError}
+                            routeProps={routeProps}
+                            perspective={perspective}
+                            animationValue={props.animationValue}
+                            animateMap={props.animateMap}
+                            screenSize={screenSize}
+                            rootUrl={rootUrlWithLang}
+                            layoutConfig={layoutConfig}
+                          />
                         </Grid>
-                      </>
-                    )
-                  }}
-                />
-              </Switch>
-            )}
-            <Route
-              path={`${rootUrlWithLang}/clientFSPlaces/federated-search`}
-              render={routeProps =>
+                      </Grid>
+                    </>
+                  )
+                }}
+              />
+            </Switch>
+          )}
+          <Route
+            path={`${rootUrlWithLang}/clientFSPlaces/federated-search`}
+            render={routeProps =>
+              <>
                 <Grid container className={classes.mainContainerClientFS}>
                   <Grid item sm={12} md={4} lg={3} className={classes.facetBarContainerClientFS}>
                     <FacetBar
@@ -566,6 +544,7 @@ const SemanticPortal = props => {
                       screenSize={screenSize}
                       showError={props.showError}
                       rootUrl={rootUrlWithLang}
+                      layoutConfig={layoutConfig}
                     />
                   </Grid>
                   <Grid item sm={12} md={8} lg={9} className={classes.resultsContainerClientFS}>
@@ -585,42 +564,45 @@ const SemanticPortal = props => {
                         clearGeoJSONLayers={props.clearGeoJSONLayers}
                         showError={props.showError}
                         rootUrl={rootUrlWithLang}
+                        layoutConfig={layoutConfig}
                       />}
                   </Grid>
-                </Grid>}
-            />
-            {/* create routes for info buttons */}
-            {/* <Route
+                </Grid>
+                <Footer layoutConfig={layoutConfig} />
+              </>}
+
+          />
+          {/* create routes for info buttons */}
+          {/* <Route
               path={`${rootUrlWithLang}/feedback`}
               render={() =>
                 <div className={classNames(classes.mainContainer, classes.textPageContainer)}>
                   <TextPage>{intl.getHTML('feedback')}</TextPage>
                 </div>}
             /> */}
-            <Route
-              path={`${rootUrlWithLang}/about`}
-              render={() =>
-                <div className={classNames(classes.mainContainer, classes.textPageContainer)}>
-                  <TextPage>
-                    {intl.getHTML('aboutThePortalPartOne')}
-                    <KnowledgeGraphMetadataTable
-                      resultClass='perspective1KnowledgeGraphMetadata'
-                      fetchKnowledgeGraphMetadata={props.fetchKnowledgeGraphMetadata}
-                      knowledgeGraphMetadata={props.perspective1.knowledgeGraphMetadata}
-                    />
-                    {intl.getHTML('aboutThePortalPartTwo')}
-                  </TextPage>
-                </div>}
-            />
-            <Route
-              path={`${rootUrlWithLang}/instructions`}
-              render={() =>
-                <div className={classNames(classes.mainContainer, classes.textPageContainer)}>
-                  <TextPage>{intl.getHTML('instructions')}</TextPage>
-                </div>}
-            />
-          </>
-        </div>
+          <Route
+            path={`${rootUrlWithLang}/about`}
+            render={() =>
+              <div className={classNames(classes.mainContainer, classes.textPageContainer)}>
+                <TextPage>
+                  {intl.getHTML('aboutThePortalPartOne')}
+                  <KnowledgeGraphMetadataTable
+                    resultClass='perspective1KnowledgeGraphMetadata'
+                    fetchKnowledgeGraphMetadata={props.fetchKnowledgeGraphMetadata}
+                    knowledgeGraphMetadata={props.perspective1.knowledgeGraphMetadata}
+                  />
+                  {intl.getHTML('aboutThePortalPartTwo')}
+                </TextPage>
+              </div>}
+          />
+          <Route
+            path={`${rootUrlWithLang}/instructions`}
+            render={() =>
+              <div className={classNames(classes.mainContainer, classes.textPageContainer)}>
+                <TextPage>{intl.getHTML('instructions')}</TextPage>
+              </div>}
+          />
+        </>
       </div>
     </MuiPickersUtilsProvider>
   )
diff --git a/src/client/index.css b/src/client/index.css
index 2e081c3d..c3b05f83 100644
--- a/src/client/index.css
+++ b/src/client/index.css
@@ -1,20 +1,15 @@
-html {
-    height: 100%;
-}
-
 body {
-    height: 100%;
     margin: 0;
-    min-width: 300px;
 }
 
-fieldset, .opacity-slider {
-    outline: 0 !important;
+@media screen and (min-width: 960px) {
+    html, body, #root, #app  {
+        height: 100%;
+    }
 }
 
-#root, #app {
-    height: 100%;
-    background-color: #bdbdbd;
+fieldset, .opacity-slider {
+    outline: 0 !important;
 }
 
 a, a:visited, a:hover, a:active {
-- 
GitLab