diff --git a/Classes/Backend/EventListener/TCA/TcaCompilation.php b/Classes/Backend/EventListener/TCA/TcaCompilation.php index 0d67314a..40a2832e 100644 --- a/Classes/Backend/EventListener/TCA/TcaCompilation.php +++ b/Classes/Backend/EventListener/TCA/TcaCompilation.php @@ -26,7 +26,8 @@ public function __invoke(AfterTcaCompilationEvent $event): void if (!empty($extconf['customFaIcons'])) { $newItems = []; foreach ( explode(',',$extconf['customFaIcons']) as $custom) { - $key = trim(end(explode(' ', $custom))); + $customArray = explode(' ', $custom); + $key = trim(end($customArray)); $newItems[] = [0 => $key, 1 => $custom]; } $tca['tt_content']['columns']['tx_t3sbootstrap_header_fontawesome']['config']['valuePicker']['items'] = $newItems; @@ -35,7 +36,8 @@ public function __invoke(AfterTcaCompilationEvent $event): void if (!empty($extconf['customHeaderClass'])) { $newItems = []; foreach ( explode(',',$extconf['customHeaderClass']) as $custom) { - $key = trim(end(explode(' ', $custom))); + $customArray = explode(' ', $custom); + $key = trim(end($customArray)); $newItems[] = [0 => $key, 1 => $custom]; } $tca['tt_content']['columns']['tx_t3sbootstrap_header_class']['config']['valuePicker']['items'] = $newItems; @@ -47,7 +49,8 @@ public function __invoke(AfterTcaCompilationEvent $event): void if (!empty($extconf['customFaIconsPages'])) { $newItems = []; foreach ( explode(',',$extconf['customFaIconsPages']) as $custom) { - $key = trim(end(explode(' ', $custom))); + $customArray = explode(' ', $custom); + $key = trim(end($customArray)); $newItems[] = [0 => $key, 1 => $custom]; } $tca['pages']['columns']['tx_t3sbootstrap_fontawesome_icon']['config']['valuePicker']['items'] = $newItems; @@ -57,7 +60,8 @@ public function __invoke(AfterTcaCompilationEvent $event): void if (!empty($extconf['customTitleColor'])) { $newItems = []; foreach ( explode(',',$extconf['customTitleColor']) as $custom) { - $key = trim(end(explode(' ', $custom))); + $customArray = explode(' ', $custom); + $key = trim(end($customArray)); $newItems[] = [0 => $key, 1 => $custom]; } $tca['pages']['columns']['tx_t3sbootstrap_titlecolor']['config']['valuePicker']['items'] = $newItems; @@ -67,7 +71,8 @@ public function __invoke(AfterTcaCompilationEvent $event): void if (!empty($extconf['customSubtitleColor'])) { $newItems = []; foreach ( explode(',',$extconf['customSubtitleColor']) as $custom) { - $key = trim(end(explode(' ', $custom))); + $customArray = explode(' ', $custom); + $key = trim(end($customArray)); $newItems[] = [0 => $key, 1 => $custom]; } $tca['pages']['columns']['tx_t3sbootstrap_subtitlecolor']['config']['valuePicker']['items'] = $newItems; @@ -79,7 +84,8 @@ public function __invoke(AfterTcaCompilationEvent $event): void if (!empty($extconf['figureClass'])) { $newItems = []; foreach ( explode(',',$extconf['figureClass']) as $custom) { - $key = trim(end(explode(' ', $custom))); + $customArray = explode(' ', $custom); + $key = trim(end($customArray)); $newItems[] = [0 => $key, 1 => $custom]; } $tca['sys_file_reference']['columns']['tx_t3sbootstrap_extra_class']['config']['valuePicker']['items'] = $newItems; @@ -89,11 +95,12 @@ public function __invoke(AfterTcaCompilationEvent $event): void if (!empty($extconf['imageClass'])) { $newItems = []; foreach ( explode(',',$extconf['imageClass']) as $custom) { - $key = trim(end(explode(' ', $custom))); + $customArray = explode(' ', $custom); + $key = trim(end($customArray)); $newItems[] = [0 => $key, 1 => $custom]; } $tca['sys_file_reference']['columns']['tx_t3sbootstrap_extra_imgclass']['config']['valuePicker']['items'] = $newItems; - } + } $event->setTca($tca); } diff --git a/Classes/Backend/FormDataProvider/FlexFormManipulation.php b/Classes/Backend/FormDataProvider/FlexFormManipulation.php index b1b07e14..a49a3ebd 100644 --- a/Classes/Backend/FormDataProvider/FlexFormManipulation.php +++ b/Classes/Backend/FormDataProvider/FlexFormManipulation.php @@ -29,73 +29,30 @@ public function addData(array $result): array # if FlexFormManipulation if ( !empty($flexforms) && !empty($result['databaseRow']['CType']) ) { - switch ($result['databaseRow']['CType']) { - case 't3sbs_card': - $flexformFile = 'cardSetting.'; - break; - case 't3sbs_carousel': - $flexformFile = 'carousel.'; - break; - case 't3sbs_button': - $flexformFile = 'button.'; - break; - case 't3sbs_mediaobject': - $flexformFile = 'mediaobject.'; - break; - case 'table': - $flexformFile = 'table.'; - break; - case 'card_wrapper': - $flexformFile = 'cardWrapper.'; - break; - case 'autoLayout_row': - $flexformFile = 'autoLayoutRow.'; - break; - case 'button_group': - $flexformFile = 'buttongroup.'; - break; - case 'container': - $flexformFile = 'container.'; - break; - case 'two_columns': - $flexformFile = 'twoColumns.'; - break; - case 'three_columns': - $flexformFile = 'threeColumns.'; - break; - case 'four_columns': - $flexformFile = 'fourColumns.'; - break; - case 'six_columns': - $flexformFile = 'sixColumns.'; - break; - case 'background_wrapper': - $flexformFile = 'backgroundWrapper.'; - break; - case 'parallax_wrapper': - $flexformFile = 'parallaxWrapper.'; - break; - case 'carousel_container': - $flexformFile = 'carouselContainer.'; - break; - case 'collapsible_accordion': - $flexformFile = 'collapse.'; - break; - case 'collapsible_container': - $flexformFile = 'collapseContainer.'; - break; - case 'modal': - $flexformFile = 'modal.'; - break; - case 'tabs_container': - $flexformFile = 'tabs.'; - break; - case 'tabs_tab': - $flexformFile = 'tabsTab.'; - break; - default: - $flexformFile = 'bootstrap.'; - } + $flexformFile = match ($result['databaseRow']['CType']) { + 't3sbs_card' => 'cardSetting.', + 't3sbs_carousel' => 'carousel.', + 't3sbs_button' => 'button.', + 't3sbs_mediaobject' => 'mediaobject.', + 'table' => 'table.', + 'card_wrapper' => 'cardWrapper.', + 'autoLayout_row' => 'autoLayoutRow.', + 'button_group' => 'buttongroup.', + 'container' => 'container.', + 'two_columns' => 'twoColumns.', + 'three_columns' => 'threeColumns.', + 'four_columns' => 'fourColumns.', + 'six_columns' => 'sixColumns.', + 'background_wrapper' => 'backgroundWrapper.', + 'parallax_wrapper' => 'parallaxWrapper.', + 'carousel_container' => 'carouselContainer.', + 'collapsible_accordion' => 'collapse.', + 'collapsible_container' => 'collapseContainer.', + 'modal' => 'modal.', + 'tabs_container' => 'tabs.', + 'tabs_tab' => 'tabsTab.', + default => 'bootstrap.', + }; $dataStructure = []; @@ -106,7 +63,7 @@ public function addData(array $result): array $addArr = []; foreach ($flexforms as $file=>$fields) { - if ( $file == $flexformFile ) { + if ( $file === $flexformFile ) { foreach ($fields as $field=>$mod) { if ( !empty($mod['add']) ) { foreach (explode(',',$mod['add']) as $add) { @@ -116,11 +73,11 @@ public function addData(array $result): array foreach ($fieldArr as $fieldName) { if (!empty($fieldName) && $fieldName !== 'array' && is_array($fieldName)) { foreach ($fieldName as $key=>$name) { - if (!empty($name['config']['type']) && $name['config']['type'] == 'select') { - if (substr($field, 0, -1) == $key) { + if (!empty($name['config']['type']) + && $name['config']['type'] === 'select' + && substr($field, 0, -1) === $key) { $addArr = ['label' => trim($add), 'value' => lcfirst(GeneralUtility::underscoredToUpperCamelCase(trim($add)))]; array_push($dataStructure['sheets'][$sheetName]['ROOT']['el'][$key]['config']['items'], $addArr); - } } } } @@ -138,10 +95,10 @@ public function addData(array $result): array foreach ($fieldArr as $fieldName) { if (!empty($fieldName) && $fieldName !== 'array' && is_array($fieldName)) { foreach ($fieldName as $key=>$name) { - if (!empty($name['config']['type']) && $name['config']['type'] == 'select') { + if (!empty($name['config']['type']) && $name['config']['type'] === 'select') { if (substr($field, 0, -1) == $key) { foreach ($name['config']['items'] as $k=>$item ) { - if (!empty($item['value']) && trim($item['value']) == trim($reduce)) { + if (!empty($item['value']) && trim($item['value']) === trim($reduce)) { unset($dataStructure['sheets'][$sheetName]['ROOT']['el'][$key]['config']['items'][$k]); } } diff --git a/Classes/Backend/Preview/DefaultPreviewRenderer.php b/Classes/Backend/Preview/DefaultPreviewRenderer.php index 06ab55d9..ec33f117 100644 --- a/Classes/Backend/Preview/DefaultPreviewRenderer.php +++ b/Classes/Backend/Preview/DefaultPreviewRenderer.php @@ -103,7 +103,7 @@ public function renderPageModulePreviewHeader(GridColumnItem $item): string } } - if ($record['CType'] == 't3sbs_gallery') { + if ($record['CType'] === 't3sbs_gallery') { $flexconfSettings .= 'columns='.$record['imagecols'].', '; if ( !empty($record['tx_t3sbootstrap_image_ratio'])) { $flexconfSettings .= 'ratio='.$record['tx_t3sbootstrap_image_ratio'].', '; @@ -114,7 +114,7 @@ public function renderPageModulePreviewHeader(GridColumnItem $item): string } $flexconfOut = ''; - + if ($record['CType'] === 't3sbs_carousel') { if ( !empty($flexconf['shift']) ) { $flexconfOut .= 'shift='.$flexconf['shift'].', '; diff --git a/Classes/Command/CustomScss.php b/Classes/Command/CustomScss.php index adf2edf5..acfb25fc 100644 --- a/Classes/Command/CustomScss.php +++ b/Classes/Command/CustomScss.php @@ -73,10 +73,10 @@ protected function execute(InputInterface $input, OutputInterface $output) if ($settings['cdn']['noZip']) { self::getBootstrapFilesNoZip($settings, $bootstrapVersion, $bootstrapScssPath); } else { - self::getBootstrapFiles($bootstrapVersion); + self::getBootstrapFiles($bootstrapVersion); } if (!file_exists(GeneralUtility::getFileAbsFileName(self::localZipPath.'scss/bootstrap.scss'))) { - self::getBootstrapFiles($settings, $bootstrapVersion, $bootstrapScssPath); + self::getBootstrapFilesNoZip($settings, $bootstrapVersion, $bootstrapScssPath); } # Custom @@ -268,6 +268,7 @@ public function getBootstrapFiles($bootstrapVersion): void parent::rmDir(GeneralUtility::getFileAbsFileName(self::localZipPath.'bootstrap-'.$bootstrapVersion)); $zipFile = GeneralUtility::getFileAbsFileName(self::localZipPath.self::localZipFile); + if (file_exists($zipFile)) unlink($zipFile); } else { throw new \InvalidArgumentException('No content from GitHub archive!', 1657464783); diff --git a/Classes/Components/Card.php b/Classes/Components/Card.php index fbb60e45..072f1103 100644 --- a/Classes/Components/Card.php +++ b/Classes/Components/Card.php @@ -9,6 +9,7 @@ use TYPO3\CMS\Core\Service\FlexFormService; use TYPO3\CMS\Backend\Utility\BackendUtility; use T3SBS\T3sbootstrap\Helper\FlexformHelper; +use T3SBS\T3sbootstrap\Utility\BackgroundImageUtility; /* * This file is part of the TYPO3 extension t3sbootstrap. @@ -164,6 +165,18 @@ public function getProcessedData(array $processedData, array $flexconf, array $p } } } + +# if ( !empty($cardData['square']['enable']) ) { +# $cardClass .= ' rounded-0 border-0'; +# } + + if ( !empty($cardData['tiling']['enable']) ) { + $cardClass = 'card tiling rounded-0 border-0 h-100'.$processedData['class']; + if (empty($processedData['data']['tx_t3sbootstrap_contextcolor'])) { + $cardClass .= ' bg-transparent'; + } + } + // header position if ( $processedData['data']['header_position'] ) { $headerPosition = $processedData['data']['header_position']; @@ -184,6 +197,17 @@ public function getProcessedData(array $processedData, array $flexconf, array $p $cardClass .= ' h-100'; } + if ( !empty($cardData['tiling']['enable']) ) { + $processedData['addmedia']['imgclass'] = ' rounded-0 '.$cardData['image']['class']; + $bgMediaQueries = '768,576'; + $bgimages = GeneralUtility::makeInstance(BackgroundImageUtility::class) + ->getBgImage($processedData['data']['uid'], 'tt_content', FALSE, FALSE, + $flexconf, FALSE, $processedData['data']['uid'],$bgMediaQueries); + if ($bgimages) { + $cardData['bgimages'] = $bgimages; + } + } + $processedData['class'] = trim($cardClass); // addmedia @@ -192,6 +216,10 @@ public function getProcessedData(array $processedData, array $flexconf, array $p $processedData['addmedia']['figureclass'] = ' text-center'; $processedData['addmedia']['figureclass'] .= !empty($flexconf['horizontal']) ? ' d-block' : ''; + if ( !empty($cardData['square']['enable']) ) { + $processedData['addmedia']['imgclass'] = 'img-fluid'; + } + $processedData['card'] = $cardData; if ( !empty($processedData['data']['imagewidth']) && !empty($flexconf['maxwidth'])) { diff --git a/Classes/Controller/ConfigController.php b/Classes/Controller/ConfigController.php index c95aa4c7..ee649ed6 100644 --- a/Classes/Controller/ConfigController.php +++ b/Classes/Controller/ConfigController.php @@ -109,8 +109,6 @@ public function listAction(bool $deleted = FALSE, bool $created = FALSE, bool $u /** * action new - * - * @return void */ public function newAction(): ResponseInterface { diff --git a/Classes/DataProcessing/BootstrapProcessor.php b/Classes/DataProcessing/BootstrapProcessor.php index 1cf96933..fef638a6 100644 --- a/Classes/DataProcessing/BootstrapProcessor.php +++ b/Classes/DataProcessing/BootstrapProcessor.php @@ -1,4 +1,5 @@ get('t3sbootstrap'); - - $cType = $processedData['data']['CType']; - $parentCType = ''; - $flexFormService = GeneralUtility::makeInstance(FlexFormService::class); - $flexconf = $flexFormService->convertFlexFormContentToArray($processedData['data']['tx_t3sbootstrap_flexform']); - $parentflexconf = []; - $parentContainer = []; - $parentUid = $processedData['data']['tx_container_parent']; - - $t3sbsElement = FALSE; - if ( str_contains(self::T3SBS_ELEMENTS.','.self::TX_CONTAINER_GRID.','.self::TX_CONTAINER, $cType) && $cType !== 'list' ) { - $t3sbsElement = TRUE; - } - - $flexformHelper = GeneralUtility::makeInstance(FlexformHelper::class); - if (is_string($cType)) { - $flexconf = $flexformHelper->addMissingElements($flexconf, $cType, $t3sbsElement); - } - if ($parentUid) { - $parentData = BackendUtility::getRecord('tt_content', $parentUid, 'uid, CType, tx_t3sbootstrap_flexform, tx_container_parent'); - $parentCType = $parentData['CType']; - $parentflexconf = $flexFormService->convertFlexFormContentToArray($parentData['tx_t3sbootstrap_flexform']); - if (is_string($parentCType)) { - $parentflexconf = $flexformHelper->addMissingElements($parentflexconf, $parentCType, $t3sbsElement); - } - $parentContainer = $parentData['tx_container_parent']; - } - - $processedData['parentCType'] = $parentCType; - $processedData['isTxContainer'] = FALSE; - $processedData['dataAnimate'] = ''; - $processedData['isAnimateCss'] = FALSE; - $processedData['animateCssRepeat'] = FALSE; - $processedData['codesnippet'] = FALSE; - $processedData['containsVideo'] = FALSE; - $processedData['containerError'] = FALSE; - $processedData['data']['configuid'] = (int)$processorConfiguration['configuid']; - $processedData['header_fontawesome'] = ''; - - $sectionMenuClass = ''; - if (!empty($contentObjectConfiguration['settings.']['sectionMenuClass'])) { - $sectionMenuClass = $contentObjectConfiguration['settings.']['sectionMenuClass']; - } - - // class - $classHelper = GeneralUtility::makeInstance(ClassHelper::class); - $class = $classHelper->getDefaultClass($processedData['data'], $flexconf, $extConf['cTypeClass'], $sectionMenuClass); - $processedData['class'] = !empty($processedData['class']) ? $processedData['class'].' '.$class : $class; - - // header class - $processedData['header'] = $classHelper->getHeaderClass($processedData['data']); - - // style - $styleHelper = GeneralUtility::makeInstance(StyleHelper::class); - $processedData['style'] = $styleHelper->getBgColor($processedData['data']); - - if ( str_contains(self::TX_CONTAINER_GRID.','.self::TX_CONTAINER, $cType) && $cType !== 'list' ) { - $isVideo = !empty($processedData['isVideo']) ? TRUE : FALSE; - $containerClass = $classHelper->getTxContainerClass($processedData['data'], $flexconf, $isVideo); - $processedData['class'] .= $containerClass ? ' '.$containerClass : ''; - $processedData['isTxContainer'] = TRUE; - } - - # - # T3SB Elements - # - if ( str_contains(self::T3SBS_ELEMENTS, $cType) ) { - if ( $cType == 't3sbs_mediaobject' ) { - $processedData = GeneralUtility::makeInstance(Mediaobject::class) - ->getProcessedData($processedData, $flexconf); - } - if ( $cType == 't3sbs_card' ) { - $processedData = GeneralUtility::makeInstance(Card::class) - ->getProcessedData($processedData, $flexconf, $parentflexconf); - } - if ( $cType == 't3sbs_carousel' ) { - $processedData = GeneralUtility::makeInstance(Carousel::class) - ->getProcessedData($processedData, $flexconf, $parentflexconf, $extConf['animateCss']); - } - if ( $cType == 't3sbs_button' ) { - $processedData = GeneralUtility::makeInstance(Button::class) - ->getProcessedData($processedData, $flexconf, $parentflexconf); - } - if ( $cType == 't3sbs_toast' ) { - $processedData = GeneralUtility::makeInstance(Toast::class) - ->getProcessedData($processedData, $flexconf); - } - if ( $cType == 't3sbs_assets' ) { - $pi_flexconf = $flexFormService->convertFlexFormContentToArray($processedData['data']['pi_flexform']); - if (!empty($pi_flexconf)) { - $processedData['assets']['jquery'] = $pi_flexconf['settings']['jquery']; - $processedData['assets']['priority'] = $pi_flexconf['settings']['priority']; - } - } - #if ( $cType == 't3sbs_fluidtemplate' ) {} - #if ( $cType == 't3sbs_gallery' ) {} - } - - # - # Grid container - # - if ( str_contains(self::TX_CONTAINER_GRID, $cType) ) { - if ( $cType == 'two_columns' ) { - $processedData = GeneralUtility::makeInstance(TwoColumns::class) - ->getProcessedData($processedData, $flexconf, $contentObjectConfiguration['settings.']['bgMediaQueries']); - } - if ( $cType == 'three_columns' ) { - $processedData = GeneralUtility::makeInstance(ThreeColumns::class) - ->getProcessedData($processedData, $flexconf); - } - if ( $cType == 'four_columns' ) { - $processedData = GeneralUtility::makeInstance(FourColumns::class) - ->getProcessedData($processedData, $flexconf); - } - if ( $cType == 'six_columns' ) { - $processedData = GeneralUtility::makeInstance(SixColumns::class) - ->getProcessedData($processedData, $flexconf); - } - if ( $cType == 'row_columns' ) { - $processedData = GeneralUtility::makeInstance(RowColumns::class) - ->getProcessedData($processedData, $flexconf); - } - } - - # - # Container/Wrapper - # - if ( str_contains(self::TX_CONTAINER, $cType) && $cType !== 'list' ) { - if ( $cType == 'card_wrapper' ) { - $processedData = GeneralUtility::makeInstance(CardWrapper::class) - ->getProcessedData($processedData, $flexconf); - } - if ( $cType == 'button_group' ) { - $processedData = GeneralUtility::makeInstance(ButtonGroup::class) - ->getProcessedData($processedData, $flexconf); - } - if ( $cType == 'background_wrapper' ) { - $processedData = GeneralUtility::makeInstance(BackgroundWrapper::class) - ->getProcessedData($processedData, $flexconf, $contentObjectConfiguration['settings.']['bgMediaQueries']); - } - if ( $cType == 'parallax_wrapper' ) { - $processedData = GeneralUtility::makeInstance(ParallaxWrapper::class) - ->getProcessedData($processedData, $flexconf); - } - if ( $cType == 'collapsible_container' ) { - $processedData = GeneralUtility::makeInstance(CollapsibleContainer::class) - ->getProcessedData($processedData, $flexconf); - } - if ( $cType == 'carousel_container' ) { - $processedData = GeneralUtility::makeInstance(CarouselContainer::class) - ->getProcessedData($processedData, $flexconf); - } - if ( $cType == 'collapsible_accordion' ) { - $processedData = GeneralUtility::makeInstance(CollapsibleAccordion::class) - ->getProcessedData($processedData, $flexconf, $parentflexconf); - } - if ( $cType == 'modal' ) { - $processedData = GeneralUtility::makeInstance(Modal::class) - ->getProcessedData($processedData, $flexconf); - } - - if ( $cType == 'tabs_container' ) { - $processedData = GeneralUtility::makeInstance(TabsContainer::class) - ->getProcessedData($processedData, $flexconf); - } - if ( $cType == 'tabs_tab' ) { - $processedData = GeneralUtility::makeInstance(TabsContainer::class) - ->getProcessedData($processedData, $flexconf); - } - if ( $cType == 'masonry_wrapper' ) { - $processedData = GeneralUtility::makeInstance(MasonryWrapper::class) - ->getProcessedData($processedData, $flexconf); - } - - if ( $cType == 'swiper_container' ) { - $processedData = GeneralUtility::makeInstance(SwiperContainer::class) - ->getProcessedData($processedData, $flexconf); - } - if ( $cType == 'toast_container' ) { - $processedData = GeneralUtility::makeInstance(ToastContainer::class) - ->getProcessedData($processedData, $flexconf, $contentObjectConfiguration['settings.']['navbarEnable']); - } - #if ( $cType == 'autoLayout_row' ) {} - #if ( $cType == 'container' ) {} - #if ( $cType == 'listGroup_wrapper' ) {} - } - - # - # default content elements - # - if ( !str_contains(self::T3SBS_ELEMENTS.','.self::TX_CONTAINER_GRID.','.self::TX_CONTAINER, $cType) ) { - if ( substr($cType, 0, 4) == 'menu' ) { - $processedData = GeneralUtility::makeInstance(Menu::class)->getProcessedData($processedData, $flexconf, $cType); - } - if ( $cType == 'table' ) { - $processedData = GeneralUtility::makeInstance(Table::class)->getProcessedData($processedData, $flexconf); - } - } - - # - # plug-ins - # - #if ( $cType == 'list' ) {} - - // media - if ( $processedData['data']['assets'] || $processedData['data']['image'] || $processedData['data']['media'] ) { - $mediaElementHelper = GeneralUtility::makeInstance(MediaElementHelper::class); - $processedData = $mediaElementHelper->getProcessedData($processedData, $extConf, $contentObjectConfiguration['settings.']['breakpoint'], $parentflexconf); - $fileRepository = GeneralUtility::makeInstance(FileRepository::class); - $fileObjects = $fileRepository->findByRelation('tt_content ', 'assets', 875); - $fileParts = []; - foreach ($fileObjects as $key=>$fileObject) { - if ( $fileObject->getType() === 4 ) { - $fileConfig = $fileObject->getStorage()->getConfiguration(); - $filePath = substr($fileConfig['basePath'], 0, -1).explode('.', $fileObject->getIdentifier())[0]; - if (file_exists($filePath.'.png')) { - $fileParts[$key]['poster'] = $filePath.'.png'; - } elseif (file_exists($filePath.'.jpg')) { - $fileParts[$key]['poster'] = $filePath.'.jpg'; - } else { - $fileParts[$key]['poster'] = ''; - } - } - } - $processedData['posters'] = $fileParts; - - if (!empty($flexconf['zoom']) || !empty($parentflexconf['zoom'])) { - $processedData['lightBox'] = TRUE; - } - // lightbox - if ( $cType == 't3sbs_gallery' || !empty($processedData['data']['image_zoom']) ) { - $processedData['lightBox'] = TRUE; - } - } - - // codesnippet - if ( $extConf['codesnippet'] && $processedData['data']['bodytext'] ) { - if (str_contains($processedData['data']['bodytext'], '
')) {
-				$processedData['codesnippet'] = TRUE;
-			}
-		}
-
-		// child of autoLayout_row
-		if ( $parentCType == 'autoLayout_row' ) {
-			$processedData['newLine'] = $flexconf['newLine'] ? TRUE : FALSE;
-			$processedData['class'] .= $classHelper->getAutoLayoutClass($flexconf);
-		}
-
-		// child of container
-		if ( $parentCType === 'container' ) {
-			$processedData['class'] .= $classHelper->getContainerClass($parentflexconf, $flexconf);
-		}
-
-		$processedData['dataAttr'] = '';
-		if (!empty($processedData['data']['tx_content_animations_animation'])) {
-			$completeAnimationSettings = $this->generateAnimationAttributeSettingsFromAnimationsArray($processedData['data']);
-			$processedData['dataAttr'] = !empty($completeAnimationSettings) ? $completeAnimationSettings : '';
-			$processedData['dataAnimate'] = '';
-			$processedData['isAnimateCss'] = FALSE;
-			$processedData['animateCssRepeat'] = FALSE;
-			$flexconf['animate'] = '';
-		}
-
-		// container class
-		$defaultHelper = GeneralUtility::makeInstance(DefaultHelper::class);
-		$processedData = $defaultHelper->getContainerClass($processedData, $extConf['container']);
-
-		// defaults
-		$processedData = $defaultHelper->getDefaults($processedData, $flexconf, $extConf,
-		 (int)$processorConfiguration['defaultHeaderType'], $processorConfiguration['contentMarginTop'], $extConf['animateCss'], $parentCType);
-
-		// trim header
-		$processedData['data']['header'] = !empty($processedData['data']['header']) ? trim($processedData['data']['header']) : '';
-
-		$processedData['style'] .= ' '.$processedData['data']['tx_t3sbootstrap_extra_style'];
-		$processedData['style'] = trim($processedData['style']);
-		$processedData['styleAttr'] = !empty($processedData['style']) ? ' style="'.$processedData['style'].'"' : '';
-		$processedData['styleInline'] = !empty($processedData['style']) ? '#c'.$processedData['data']['uid'].' {'.$processedData['style'].'}' : '';
-
-		$processedData['trimClass'] = !empty(trim($processedData['class'])) ? trim($processedData['class']) : '';
-		$processedData['class'] = !empty($processedData['trimClass']) ? ' '.$processedData['trimClass'] : '';
-		$processedData['classAttr'] = !empty($processedData['trimClass']) ? ' class="'.trim($processedData['class']).'"' : '';
-
-		if ( !empty($processedData['data']['tx_t3sbootstrap_header_fontawesome']) ) {
-			$processedData['headerFontawesome'] = ' ';
-		}
-
-		return $processedData;
-	}
-
-
-
-	/**
-	 * @param array $animationSettingsArray
-	 * @return string
-	 */
-	private function generateAnimationAttributeSettingsFromAnimationsArray(array $animationSettingsArray)
-	{
-		$animationSettings = '';
-
-		foreach ($animationSettingsArray as $key => $value) {
-			if (str_starts_with($key, 'tx_content_animations_')) {
-				if ($key == 'tx_content_animations_animation' ) {
-					$newphrase = str_replace('tx_content_animations_animation', 'data-aos', $key);
-					$animationSettings .= $newphrase . '="' . $value . '" ';
-				} else {
-					$newphrase = str_replace('tx_content_animations_', 'data-aos-', $key);
-					$animationSettings .= $newphrase . '="' . $value . '" ';
-				}
-			}
-		}
-		return ' '.$animationSettings;
-	}
-
-
+    public const TX_CONTAINER_GRID = 'two_columns,three_columns,four_columns,six_columns,row_columns';
+    public const T3SBS_ELEMENTS = 't3sbs_mediaobject,t3sbs_card,t3sbs_carousel,t3sbs_button,t3sbs_fluidtemplate,t3sbs_gallery,t3sbs_toast,t3sbs_assets';
+    public const TX_CONTAINER = 'button_group,background_wrapper,parallax_wrapper,autoLayout_row,container,carousel_container,collapsible_container,collapsible_accordion,modal,tabs_container,tabs_tab,listGroup_wrapper,masonry_wrapper,swiper_container,toast_container,card_wrapper';
+
+    /**
+     * Process data
+     *
+     * @param ContentObjectRenderer $cObj The data of the content element or page
+     * @param array $contentObjectConfiguration The configuration of Content Object
+     * @param array $processorConfiguration The configuration of this processor
+     * @param array $processedData Key/value store of processed data (e.g. to be passed to a Fluid View)
+     * @return array the processed data as key/value store
+     */
+    public function process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData)
+    {
+        if (empty($processedData['data']['CType'])) {
+            return $processedData;
+        }
+
+        $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('t3sbootstrap');
+
+        $cType = $processedData['data']['CType'];
+        $parentCType = '';
+        $flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
+        $flexconf = $flexFormService->convertFlexFormContentToArray($processedData['data']['tx_t3sbootstrap_flexform']);
+        $parentflexconf = [];
+        $parentContainer = [];
+        $parentUid = $processedData['data']['tx_container_parent'];
+
+        $t3sbsElement = false;
+        if (str_contains(self::T3SBS_ELEMENTS.','.self::TX_CONTAINER_GRID.','.self::TX_CONTAINER, $cType) && $cType !== 'list') {
+            $t3sbsElement = true;
+        }
+
+        $flexformHelper = GeneralUtility::makeInstance(FlexformHelper::class);
+        if (is_string($cType)) {
+            $flexconf = $flexformHelper->addMissingElements($flexconf, $cType, $t3sbsElement);
+        }
+        if ($parentUid) {
+            $parentData = BackendUtility::getRecord('tt_content', $parentUid, 'uid, CType, tx_t3sbootstrap_flexform, tx_container_parent');
+            $parentCType = $parentData['CType'];
+            $parentflexconf = $flexFormService->convertFlexFormContentToArray($parentData['tx_t3sbootstrap_flexform']);
+            if (is_string($parentCType)) {
+                $parentflexconf = $flexformHelper->addMissingElements($parentflexconf, $parentCType, $t3sbsElement);
+            }
+            $parentContainer = $parentData['tx_container_parent'];
+        }
+
+        $processedData['parentCType'] = $parentCType;
+        $processedData['isTxContainer'] = false;
+        $processedData['dataAnimate'] = '';
+        $processedData['isAnimateCss'] = false;
+        $processedData['animateCssRepeat'] = false;
+        $processedData['codesnippet'] = false;
+        $processedData['containsVideo'] = false;
+        $processedData['containerError'] = false;
+        $processedData['data']['configuid'] = (int)$processorConfiguration['configuid'];
+        $processedData['header_fontawesome'] = '';
+
+        $sectionMenuClass = '';
+        if (!empty($contentObjectConfiguration['settings.']['sectionMenuClass'])) {
+            $sectionMenuClass = $contentObjectConfiguration['settings.']['sectionMenuClass'];
+        }
+
+        // class
+        $classHelper = GeneralUtility::makeInstance(ClassHelper::class);
+        $class = $classHelper->getDefaultClass($processedData['data'], $flexconf, $extConf['cTypeClass'], $sectionMenuClass);
+        $processedData['class'] = !empty($processedData['class']) ? $processedData['class'].' '.$class : $class;
+
+        // header class
+        $processedData['header'] = $classHelper->getHeaderClass($processedData['data']);
+
+        // style
+        $styleHelper = GeneralUtility::makeInstance(StyleHelper::class);
+        $processedData['style'] = $styleHelper->getBgColor($processedData['data']);
+
+        if (str_contains(self::TX_CONTAINER_GRID.','.self::TX_CONTAINER, $cType) && $cType !== 'list') {
+            $isVideo = !empty($processedData['isVideo']) ? true : false;
+            $containerClass = $classHelper->getTxContainerClass($processedData['data'], $flexconf, $isVideo);
+            $processedData['class'] .= $containerClass ? ' '.$containerClass : '';
+            $processedData['isTxContainer'] = true;
+        }
+
+        #
+        # T3SB Elements
+        #
+        if (str_contains(self::T3SBS_ELEMENTS, $cType)) {
+            if ($cType == 't3sbs_mediaobject') {
+                $processedData = GeneralUtility::makeInstance(Mediaobject::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+            if ($cType == 't3sbs_card') {
+                $processedData = GeneralUtility::makeInstance(Card::class)
+                ->getProcessedData($processedData, $flexconf, $parentflexconf);
+            }
+            if ($cType == 't3sbs_carousel') {
+                $processedData = GeneralUtility::makeInstance(Carousel::class)
+                ->getProcessedData($processedData, $flexconf, $parentflexconf, $extConf['animateCss']);
+            }
+            if ($cType == 't3sbs_button') {
+                $processedData = GeneralUtility::makeInstance(Button::class)
+                ->getProcessedData($processedData, $flexconf, $parentflexconf);
+            }
+            if ($cType == 't3sbs_toast') {
+                $processedData = GeneralUtility::makeInstance(Toast::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+            if ($cType == 't3sbs_assets') {
+                $pi_flexconf = $flexFormService->convertFlexFormContentToArray($processedData['data']['pi_flexform']);
+                if (!empty($pi_flexconf)) {
+                    $processedData['assets']['jquery'] = $pi_flexconf['settings']['jquery'];
+                    $processedData['assets']['priority'] = $pi_flexconf['settings']['priority'];
+                }
+            }
+            #if ( $cType == 't3sbs_fluidtemplate' ) {}
+            #if ( $cType == 't3sbs_gallery' ) {}
+        }
+
+        #
+        # Grid container
+        #
+        if (str_contains(self::TX_CONTAINER_GRID, $cType)) {
+            if ($cType == 'two_columns') {
+                $processedData = GeneralUtility::makeInstance(TwoColumns::class)
+                ->getProcessedData($processedData, $flexconf, $contentObjectConfiguration['settings.']['bgMediaQueries']);
+            }
+            if ($cType == 'three_columns') {
+                $processedData = GeneralUtility::makeInstance(ThreeColumns::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+            if ($cType == 'four_columns') {
+                $processedData = GeneralUtility::makeInstance(FourColumns::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+            if ($cType == 'six_columns') {
+                $processedData = GeneralUtility::makeInstance(SixColumns::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+            if ($cType == 'row_columns') {
+                $processedData = GeneralUtility::makeInstance(RowColumns::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+        }
+
+        #
+        # Container/Wrapper
+        #
+        if (str_contains(self::TX_CONTAINER, $cType) && $cType !== 'list') {
+            if ($cType == 'card_wrapper') {
+                $processedData = GeneralUtility::makeInstance(CardWrapper::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+            if ($cType == 'button_group') {
+                $processedData = GeneralUtility::makeInstance(ButtonGroup::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+            if ($cType == 'background_wrapper') {
+                $processedData = GeneralUtility::makeInstance(BackgroundWrapper::class)
+                ->getProcessedData($processedData, $flexconf, $contentObjectConfiguration['settings.']['bgMediaQueries']);
+            }
+            if ($cType == 'parallax_wrapper') {
+                $processedData = GeneralUtility::makeInstance(ParallaxWrapper::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+            if ($cType == 'collapsible_container') {
+                $processedData = GeneralUtility::makeInstance(CollapsibleContainer::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+            if ($cType == 'carousel_container') {
+                $processedData = GeneralUtility::makeInstance(CarouselContainer::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+            if ($cType == 'collapsible_accordion') {
+                $processedData = GeneralUtility::makeInstance(CollapsibleAccordion::class)
+                ->getProcessedData($processedData, $flexconf, $parentflexconf);
+            }
+            if ($cType == 'modal') {
+                $processedData = GeneralUtility::makeInstance(Modal::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+
+            if ($cType == 'tabs_container') {
+                $processedData = GeneralUtility::makeInstance(TabsContainer::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+            if ($cType == 'tabs_tab') {
+                $processedData = GeneralUtility::makeInstance(TabsContainer::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+            if ($cType == 'masonry_wrapper') {
+                $processedData = GeneralUtility::makeInstance(MasonryWrapper::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+
+            if ($cType == 'swiper_container') {
+                $processedData = GeneralUtility::makeInstance(SwiperContainer::class)
+                ->getProcessedData($processedData, $flexconf);
+            }
+            if ($cType == 'toast_container') {
+                $processedData = GeneralUtility::makeInstance(ToastContainer::class)
+                ->getProcessedData($processedData, $flexconf, $contentObjectConfiguration['settings.']['navbarEnable']);
+            }
+            #if ( $cType == 'autoLayout_row' ) {}
+            #if ( $cType == 'container' ) {}
+            #if ( $cType == 'listGroup_wrapper' ) {}
+        }
+
+        #
+        # default content elements
+        #
+        if (!str_contains(self::T3SBS_ELEMENTS.','.self::TX_CONTAINER_GRID.','.self::TX_CONTAINER, $cType)) {
+            if (substr($cType, 0, 4) == 'menu') {
+                $processedData = GeneralUtility::makeInstance(Menu::class)->getProcessedData($processedData, $flexconf, $cType);
+            }
+            if ($cType == 'table') {
+                $processedData = GeneralUtility::makeInstance(Table::class)->getProcessedData($processedData, $flexconf);
+            }
+        }
+
+        #
+        # plug-ins
+        #
+        #if ( $cType == 'list' ) {}
+
+        // media
+        if ($processedData['data']['assets'] || $processedData['data']['image'] || $processedData['data']['media']) {
+            $mediaElementHelper = GeneralUtility::makeInstance(MediaElementHelper::class);
+            $processedData = $mediaElementHelper->getProcessedData($processedData, $extConf, $contentObjectConfiguration['settings.']['breakpoint'], $parentflexconf);
+            $fileRepository = GeneralUtility::makeInstance(FileRepository::class);
+            $fileObjects = $fileRepository->findByRelation('tt_content ', 'assets', $processedData['data']['uid']);
+            $fileParts = [];
+            $processedData['addmedia']['ratioClass'] = 'ratio-16x9';
+            foreach ($fileObjects as $key=>$fileObject) {
+                if ($fileObject->getType() === 4) {
+                    $fileConfig = $fileObject->getStorage()->getConfiguration();
+                    $filePath = substr($fileConfig['basePath'], 0, -1).explode('.', $fileObject->getIdentifier())[0];
+                    if (file_exists($filePath.'.png')) {
+                        $fileParts[$key]['poster'] = $filePath.'.png';
+                    } elseif (file_exists($filePath.'.jpg')) {
+                        $fileParts[$key]['poster'] = $filePath.'.jpg';
+                    } else {
+                        $fileParts[$key]['poster'] = '';
+                    }
+                    if (array_key_exists('ratio', $extConf) && $extConf['ratio'] === '1'
+                    && !empty($fileObject->getProperties()['tx_t3sbootstrap_video_ratio'])) {
+                        $properties = $fileObject->getProperties();
+                        if (str_contains($properties['tx_t3sbootstrap_video_ratio'], ':')) {
+                            $ratioArr = explode(':', $properties['tx_t3sbootstrap_video_ratio']);
+                        } elseif (str_contains($properties['tx_t3sbootstrap_video_ratio'], 'x')) {
+                            $ratioArr = explode('x', $properties['tx_t3sbootstrap_video_ratio']);
+                        }
+                        $x = $ratioArr[0].'x'.$ratioArr[1];
+                        $y = $ratioArr[1].' / '.$ratioArr[0].' * 100%';
+                        $processedData['addmedia']['ratioCalcCss'] = '.ratio-'.$x.'{--bs-aspect-ratio:calc('.$y.');}';
+                        $processedData['addmedia']['ratioClass'] = 'ratio-'.$x;
+                    }
+                }
+            }
+            $processedData['posters'] = $fileParts;
+
+            if (!empty($flexconf['zoom']) || !empty($parentflexconf['zoom'])) {
+                $processedData['lightBox'] = true;
+            }
+            // lightbox
+            if ($cType == 't3sbs_gallery' || !empty($processedData['data']['image_zoom'])) {
+                $processedData['lightBox'] = true;
+            }
+        }
+
+        // codesnippet
+        if (array_key_exists('codesnippet', $extConf) && $extConf['codesnippet'] === '1' && $processedData['data']['bodytext']) {
+            if (str_contains($processedData['data']['bodytext'], '
')) {
+                $processedData['codesnippet'] = true;
+            }
+        }
+
+        // child of autoLayout_row
+        if ($parentCType == 'autoLayout_row') {
+            $processedData['newLine'] = $flexconf['newLine'] ? true : false;
+            $processedData['class'] .= $classHelper->getAutoLayoutClass($flexconf);
+        }
+
+        // child of container
+        if ($parentCType === 'container') {
+            $processedData['class'] .= $classHelper->getContainerClass($parentflexconf, $flexconf);
+        }
+
+        $processedData['dataAttr'] = '';
+        if (!empty($processedData['data']['tx_content_animations_animation'])) {
+            $completeAnimationSettings = $this->generateAnimationAttributeSettingsFromAnimationsArray($processedData['data']);
+            $processedData['dataAttr'] = !empty($completeAnimationSettings) ? $completeAnimationSettings : '';
+            $processedData['dataAnimate'] = '';
+            $processedData['isAnimateCss'] = false;
+            $processedData['animateCssRepeat'] = false;
+            $flexconf['animate'] = '';
+        }
+
+        // container class
+        $defaultHelper = GeneralUtility::makeInstance(DefaultHelper::class);
+        $processedData = $defaultHelper->getContainerClass($processedData, $extConf['container']);
+
+        // defaults
+        $processedData = $defaultHelper->getDefaults(
+            $processedData,
+            $flexconf,
+            $extConf,
+            (int)$processorConfiguration['defaultHeaderType'],
+            $processorConfiguration['contentMarginTop'],
+            $extConf['animateCss'],
+            $parentCType
+        );
+
+        // trim header
+        $processedData['data']['header'] = !empty($processedData['data']['header']) ? trim($processedData['data']['header']) : '';
+
+        $processedData['style'] .= ' '.$processedData['data']['tx_t3sbootstrap_extra_style'];
+        $processedData['style'] = trim($processedData['style']);
+        $processedData['styleAttr'] = !empty($processedData['style']) ? ' style="'.$processedData['style'].'"' : '';
+        $processedData['styleInline'] = !empty($processedData['style']) ? '#c'.$processedData['data']['uid'].' {'.$processedData['style'].'}' : '';
+
+        $processedData['trimClass'] = !empty(trim($processedData['class'])) ? trim($processedData['class']) : '';
+        $processedData['class'] = !empty($processedData['trimClass']) ? ' '.$processedData['trimClass'] : '';
+        $processedData['classAttr'] = !empty($processedData['trimClass']) ? ' class="'.trim($processedData['class']).'"' : '';
+
+        if (!empty($processedData['data']['tx_t3sbootstrap_header_fontawesome'])) {
+            $processedData['headerFontawesome'] = ' ';
+        }
+
+        return $processedData;
+    }
+
+
+
+    /**
+     * @param array $animationSettingsArray
+     * @return string
+     */
+    private function generateAnimationAttributeSettingsFromAnimationsArray(array $animationSettingsArray)
+    {
+        $animationSettings = '';
+
+        foreach ($animationSettingsArray as $key => $value) {
+            if (str_starts_with($key, 'tx_content_animations_')) {
+                if ($key == 'tx_content_animations_animation') {
+                    $newphrase = str_replace('tx_content_animations_animation', 'data-aos', $key);
+                    $animationSettings .= $newphrase . '="' . $value . '" ';
+                } else {
+                    $newphrase = str_replace('tx_content_animations_', 'data-aos-', $key);
+                    $animationSettings .= $newphrase . '="' . $value . '" ';
+                }
+            }
+        }
+        return ' '.$animationSettings;
+    }
 }
diff --git a/Classes/Layouts/TwoColumns.php b/Classes/Layouts/TwoColumns.php
index 94154226..40161d68 100644
--- a/Classes/Layouts/TwoColumns.php
+++ b/Classes/Layouts/TwoColumns.php
@@ -9,6 +9,7 @@
 use T3SBS\T3sbootstrap\Layouts\Grid;
 use T3SBS\T3sbootstrap\Layouts\Gutters;
 
+use TYPO3\CMS\Core\Service\FlexFormService;
 /*
  * This file is part of the TYPO3 extension t3sbootstrap.
  *
@@ -26,6 +27,20 @@ public function getProcessedData(array $processedData, array $flexconf, string $
 		$processedData = GeneralUtility::makeInstance(Gutters::class)->getGutters($processedData, $flexconf);
 		$processedData = GeneralUtility::makeInstance(Grid::class)->getGrid($processedData, $flexconf);
 
+		$flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
+		foreach ($processedData as $key=>$data) {
+			if ($key == 'col_221' || $key == 'col_222') {
+				if (is_array($data) && !empty($data[0]['tx_t3sbootstrap_flexform'])) {
+					$flexconf = $flexFormService->convertFlexFormContentToArray($data[0]['tx_t3sbootstrap_flexform']);
+					if (!empty($flexconf['tiling']['enable'])) {
+						// cards with tiling enabled inside
+						$processedData['gutters'] = ' gx-0 gy-0';
+						break;
+					}
+				}	
+			}
+		}
+
 		$processedData['style'] .= !empty($flexconf['colHeight']) ? ' min-height: '.$flexconf['colHeight'].'px;' : '';
 		$processedData['verticalAlign'] = !empty($flexconf['colHeight'])
 		 && !empty($flexconf['verticalAlign']) ? ' d-flex align-items-' . $flexconf['verticalAlign'] : '';
diff --git a/Classes/UserFunction/TagModifier.php b/Classes/UserFunction/TagModifier.php
deleted file mode 100644
index e9546418..00000000
--- a/Classes/UserFunction/TagModifier.php
+++ /dev/null
@@ -1,32 +0,0 @@
- 0) {
-            $href = preg_replace('/###(@[^?]*)/', '###', $href) ?? $href;
-        }
-        return $href;
-    }
-}
\ No newline at end of file
diff --git a/Classes/Utility/BackgroundImageUtility.php b/Classes/Utility/BackgroundImageUtility.php
index 6e82d201..8b1b4c01 100644
--- a/Classes/Utility/BackgroundImageUtility.php
+++ b/Classes/Utility/BackgroundImageUtility.php
@@ -13,7 +13,6 @@
 use TYPO3\CMS\Core\Page\AssetCollector;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
-use TYPO3\CMS\Core\Utility\PathUtility;
 
 /*
  * This file is part of the TYPO3 extension t3sbootstrap.
@@ -65,18 +64,16 @@ public function getBgImage(
 		if ( ExtensionManagementUtility::isLoaded('webp') ) {
 			$webp = true;
 		}
-
+		
 		if ( count($filesFromRepository) > 1 && $body == FALSE ) {
-
 			if ( !empty($flexconf['bgimagePosition']) && ( $flexconf['bgimagePosition'] == 1 || $flexconf['bgimagePosition'] == 2 ) ) {
 				// bg-images in two-columns
 				// in the case if two images available but only one is selected in the flexform
 				$file = $filesFromRepository[0];
 				$image = $this->imageService->getImage($file->getOriginalFile()->getUid(), $file->getOriginalFile(), 1);
 				$bgImages = $this->generateSrcsetImages($file, $image);
-				$imageUri_mobile = $bgImages[576];
 				$imageUri_mobile = $webp ? $bgImages[576].'.webp' : $bgImages[576];
-				$css .= $this->generateCss('s'.$uid.'-'.$flexconf['bgimagePosition'], $file, $image, $flexconf, FALSE, $bgMediaQueries);
+				$css .= $this->generateCss('s'.$uid.'-'.$flexconf['bgimagePosition'], $file, $image, $webp, $flexconf, FALSE, $bgMediaQueries);
 
 			} else {
 				// slider in jumbotron or two bg-images in two-columns
@@ -88,7 +85,7 @@ public function getBgImage(
 					$image[$fileKey] = $this->imageService->getImage((string)$file->getOriginalFile()->getUid(), $file->getOriginalFile(), true);
 					$bgImages[$fileKey] = $this->generateSrcsetImages($file, $image[$fileKey]);
 					$imageUri_mobile[$fileKey] = $webp ? $bgImages[$fileKey][576].'.webp' : $bgImages[$fileKey][576];
-					$css .= $this->generateCss('s'.$uid.'-'.$fileKey, $file, $image[$fileKey], $flexconf, FALSE, $bgMediaQueries);
+					$css .= $this->generateCss('s'.$uid.'-'.$fileKey, $file, $image[$fileKey], $webp, $flexconf, FALSE, $bgMediaQueries);
 				}
 			}
 		} else {
@@ -101,9 +98,9 @@ public function getBgImage(
 					$uid = $uid . '-' . $flexconf['bgimagePosition'];
 				}
 				if ($jumbotron) {
-					$css = $this->generateCss('s'.$uid, $file, $image, $flexconf, FALSE, $bgMediaQueries);
+					$css = $this->generateCss('s'.$uid, $file, $image, $webp, $flexconf, FALSE, $bgMediaQueries);
 				} elseif ($body) {
-					$css = $this->generateCss('page-'.$uid, $file, $image, $flexconf, TRUE, $bgMediaQueries);
+					$css = $this->generateCss('page-'.$uid, $file, $image, $webp, $flexconf, TRUE, $bgMediaQueries);
 				} else {
 					if ( !empty($flexconf['enableAutoheight']) ) {
 						if ( $flexconf['addHeight'] ) {
@@ -112,9 +109,9 @@ public function getBgImage(
 							GeneralUtility::makeInstance(AssetCollector::class)
 								  ->addInlineJavaScript('addheight-'.$uid, $inline);
 						}
-						$css = $this->generateCss('bg-img-'.$uid, $file, $image, $flexconf, FALSE, $bgMediaQueries);
+						$css = $this->generateCss('bg-img-'.$uid, $file, $image, $webp, $flexconf, FALSE, $bgMediaQueries);
 					} else {
-						$css = $this->generateCss('s'.$uid, $file, $image, $flexconf, FALSE, $bgMediaQueries);
+						$css = $this->generateCss('s'.$uid, $file, $image, $webp, $flexconf, FALSE, $bgMediaQueries);
 					}
 				}
 				$bgImages = $this->generateSrcsetImages($file, $image);
@@ -149,6 +146,7 @@ private function generateCss(
 		string $uid,
 		FileReference $file,
 		File $image,
+		bool $webp,
 		array $flexconf=[],
 		bool $body=FALSE,
 		string $bgMediaQueries='2560,1920,1200,992,768,576'
@@ -158,11 +156,6 @@ private function generateCss(
 		$processingInstructions = ['crop' => $file instanceof FileReference ? $file->getReferenceProperty('crop') : null];
 		$cropVariantCollection = CropVariantCollection::create((string) $processingInstructions['crop']);
 
-		$webp = false;
-		if ( ExtensionManagementUtility::isLoaded('webp') ) {
-			$webp = true;
-		}
-
 		$css = '';
 		$mediaQueries = explode(',', $bgMediaQueries);
 		$minWidth = (int)$mediaQueries[0];
diff --git a/Classes/Wrapper/BackgroundWrapper.php b/Classes/Wrapper/BackgroundWrapper.php
index 345657b2..240b4d21 100644
--- a/Classes/Wrapper/BackgroundWrapper.php
+++ b/Classes/Wrapper/BackgroundWrapper.php
@@ -44,9 +44,9 @@ public function getProcessedData(array $processedData, array $flexconf, string $
 					$processedData['youtube'] = TRUE;
 					$processedData['vimeo'] = FALSE;
 					$processedData['isVideo'] = TRUE;
-					$processedData['contentPosition'] = $flexconf['contentPosition'];
-					$processedData['ytVideo']['bgHeight'] = $flexconf['bgHeight'];
-					$processedData['ytVideo']['ytshift'] = $flexconf['ytshift'];
+					$processedData['contentPosition'] = !empty($flexconf['contentPosition']) ? $flexconf['contentPosition'] : 'align-self-center';
+					$processedData['ytVideo']['bgHeight'] = !empty($flexconf['bgHeight']) ? $flexconf['bgHeight'] : '';
+					$processedData['ytVideo']['ytshift'] = !empty($flexconf['ytshift']) ? $flexconf['ytshift'] : '';
 					$processedData['videoAutoPlay'] = $file->getProperties()['autoplay'];
 					$youTubeRenderer = GeneralUtility::makeInstance(YouTubeRenderer::class);
 					$processedData['videoId'] = $youTubeRenderer->render($file);
@@ -56,9 +56,9 @@ public function getProcessedData(array $processedData, array $flexconf, string $
 						$processedData['vimeo'] = TRUE;
 						$processedData['youtube'] = FALSE;
 						$processedData['isVideo'] = TRUE;
-						$processedData['contentPosition'] = $flexconf['contentPosition'];
-						$processedData['ytVideo']['bgHeight'] = $flexconf['bgHeight'];
-						$processedData['ytVideo']['ytshift'] = $flexconf['ytshift'];
+						$processedData['contentPosition'] = !empty($flexconf['contentPosition']) ? $flexconf['contentPosition'] : 'align-self-center';
+						$processedData['ytVideo']['bgHeight'] = !empty($flexconf['bgHeight']) ? $flexconf['bgHeight'] : '';
+						$processedData['ytVideo']['ytshift'] = !empty($flexconf['ytshift']) ? $flexconf['ytshift'] : '';
 						$processedData['videoAutoPlay'] = $file->getProperties()['autoplay'];
 						$youTubeRenderer = GeneralUtility::makeInstance(YouTubeRenderer::class);
 						$processedData['videoId'] = $youTubeRenderer->render($file);
@@ -107,8 +107,6 @@ public function getProcessedData(array $processedData, array $flexconf, string $
 
 			} elseif ( $file->getType() === 2 ) {
 			// IMAGE
-
-
 				// orig. image option in flexform
 				if (!empty($flexconf['origImage'])) {
 					$processedData['file'] = $file;
diff --git a/Classes/Wrapper/CardWrapper.php b/Classes/Wrapper/CardWrapper.php
index 82d5bda5..557dca49 100644
--- a/Classes/Wrapper/CardWrapper.php
+++ b/Classes/Wrapper/CardWrapper.php
@@ -24,7 +24,6 @@ class CardWrapper implements SingletonInterface
 	 */
 	public function getProcessedData(array $processedData, array $flexconf): array
 	{
-		$processedData['visibleCards'] = !empty($flexconf['visibleCards']) ? (int)$flexconf['visibleCards'] : 4;
 		$processedData['gutter'] = !empty($flexconf['gutter']) ? (int)$flexconf['gutter'] : 0;
 
 		$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
@@ -114,15 +113,16 @@ public function getProcessedData(array $processedData, array $flexconf): array
 
 			// swiperjs
 			if ($flexconf['card_wrapper'] == 'slider') {
-				$processedData['visibleCards'] = $flexconf['visibleCards'] ? (int)$flexconf['visibleCards'] : 3;
+				$processedData['visibleCards'] = !empty($flexconf['visibleCards']) ? (int)$flexconf['visibleCards'] : 3;
 				$processedData['cols'] = floor(12 / $processedData['visibleCards']);
 				$processedData['width'] = $flexconf['width'];
 				$processedData['ratio'] = $flexconf['ratio'];
-				$processedData['slidesPerView'] = (int)$flexconf['slidesPerView'] ?: 4;
+				$processedData['slidesPerView'] = (int)$flexconf['slidesPerView'] ?: 0;
+				$processedData['breakpoints10'] = !empty($flexconf['breakpoints10']) ? (int)$flexconf['breakpoints10'] : 1;
 				$processedData['breakpoints576'] = (int)$flexconf['breakpoints576'] ?: 2;
 				$processedData['breakpoints768'] = (int)$flexconf['breakpoints768'] ?: 3;
 				$processedData['breakpoints992'] = (int)$flexconf['breakpoints992'] ?: 4;
-				$processedData['slidesPerGroup'] = (int)$flexconf['slidesPerGroup'];
+				$processedData['slidesPerGroup'] = (int)$flexconf['slidesPerGroup'] ?: 1;
 				$processedData['spaceBetween'] = (int)$flexconf['spaceBetween'];
 				$processedData['loop'] = (int)$flexconf['loop'];
 				$processedData['navigation'] = (int)$flexconf['navigation'];
diff --git a/Classes/Wrapper/CarouselContainer.php b/Classes/Wrapper/CarouselContainer.php
index e35bf9bb..0fe81e75 100644
--- a/Classes/Wrapper/CarouselContainer.php
+++ b/Classes/Wrapper/CarouselContainer.php
@@ -24,7 +24,7 @@ public function getProcessedData(array $processedData, array $flexconf): array
 	{
 		$processedData['maxWidth'] = $flexconf['width'] ? $flexconf['width'].'px' : '1440px';
 		$processedData['interval'] = $flexconf['interval'];
-		$processedData['darkVariant'] = $flexconf['darkVariant'];
+		$processedData['darkVariant'] = !empty($flexconf['darkVariant']) ? $flexconf['darkVariant'] : 'light';
 		$processedData['carouselFade'] = !empty($flexconf['carouselFade']) ? ' carousel-fade': '';
 		$processedData['carouselFade'] .= !empty($flexconf['darkVariant']) ? ' carousel-dark': '';
 		$processedData['thumbnails'] = !empty($flexconf['thumbnails']) ? true : false;
diff --git a/Classes/Wrapper/SwiperContainer.php b/Classes/Wrapper/SwiperContainer.php
index 6a1103af..fcbbcde0 100644
--- a/Classes/Wrapper/SwiperContainer.php
+++ b/Classes/Wrapper/SwiperContainer.php
@@ -1,4 +1,5 @@
 getQueryBuilderForTable('tt_content');
-		$statement = $queryBuilder
-			->select('uid')
-			->from('tt_content')
-			->where(
-				$queryBuilder->expr()->eq('tx_container_parent', $queryBuilder->createNamedParameter($processedData['data']['uid'], \PDO::PARAM_INT))
-			)
-			->executeQuery()
-			->fetchAll();
+        $processedData['swiperCss'] = !empty($flexconf['swiperCss']) ? $flexconf['swiperCss'] : '';
+        $processedData['swiperJs'] = $flexconf['swiperJs'] ?? '';
+        $processedData['customSwiperJs'] = $flexconf['customSwiperJs'] ?? '';
+        $processedData['useCustomSwiperJs'] = $flexconf['useCustomSwiperJs'] ?? false;
+        $processedData['sliderStyle'] = $flexconf['sliderStyle'];
+        $processedData['width'] = $flexconf['width'];
+        $processedData['ratio'] = $flexconf['ratio'];
+        $processedData['slidesPerView'] = (int)$flexconf['slidesPerView'] ?: 0;
+        $processedData['breakpoints10'] = !empty($flexconf['breakpoints10']) ? (int)$flexconf['breakpoints10'] : 1;
+        $processedData['breakpoints576'] = (int)$flexconf['breakpoints576'] ?: 2;
+        $processedData['breakpoints768'] = (int)$flexconf['breakpoints768'] ?: 3;
+        $processedData['breakpoints992'] = (int)$flexconf['breakpoints992'] ?: 4;
+        $processedData['slidesPerGroup'] = (int)$flexconf['slidesPerGroup'] ?: 1;
+        $processedData['spaceBetween'] = (int)$flexconf['spaceBetween'];
+        $processedData['loop'] = (int)$flexconf['loop'];
+        $processedData['zoom'] = !empty($flexconf['zoom']) ? (int)$flexconf['zoom'] : 0;
+        $processedData['navigation'] = (int)$flexconf['navigation'];
+        $processedData['pagination'] = (int)$flexconf['pagination'];
+        $processedData['autoplay'] = (int)$flexconf['autoplay'];
+        $processedData['delay'] = !empty($flexconf['autoplay']) ? (int)$flexconf['delay'] : 99999999;
+        $processedData['origImage'] = !empty($flexconf['origImage']) ? $flexconf['origImage'] : '';
 
-		$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
-		foreach($statement as $element) {
-			$filesFromRepository[$element['uid']] = $fileRepository->findByRelation('tt_content', 'assets', $element['uid']);
-		}
-		$processedData['swiperSlides'] = $filesFromRepository;
+        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
+        $queryBuilder = $connectionPool->getQueryBuilderForTable('tt_content');
+        $statement = $queryBuilder
+            ->select('uid')
+            ->from('tt_content')
+            ->where(
+                $queryBuilder->expr()->eq('tx_container_parent', $queryBuilder->createNamedParameter($processedData['data']['uid'], \PDO::PARAM_INT))
+            )
+            ->executeQuery()
+            ->fetchAll();
 
-		return $processedData;
-	}
+        $fileRepository = GeneralUtility::makeInstance(FileRepository::class);
+        foreach ($statement as $element) {
+            $filesFromRepository[$element['uid']] = $fileRepository->findByRelation('tt_content', 'assets', $element['uid']);
+        }
+        $processedData['swiperSlides'] = $filesFromRepository;
 
+        return $processedData;
+    }
 }
diff --git a/Configuration/FlexForms/CardSetting.xml b/Configuration/FlexForms/CardSetting.xml
index 3cb2f699..f9dd4a6e 100644
--- a/Configuration/FlexForms/CardSetting.xml
+++ b/Configuration/FlexForms/CardSetting.xml
@@ -1,1265 +1,1430 @@
 
-	
-		1
-	
 	
 		
 			
-				Card Settings
+				
+					Card Settings
+				
 				array
 				
 					
-						
-						
-							select
-							selectSingle
-							
-								
-									None
-									0
-								
-								
-									zoom and border
-									one
-								
-							
-							0
-						
+						
+							
+							FIELD:sDEF.tiling.enable:=:0
+							
+								select
+								selectSingle
+								
+									
+										None
+										0
+									
+									
+										zoom and border
+										one
+									
+								
+								0
+							
+						
 					
 					
-						
-						USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->cardWrapperParent
-						
-							check
-						
+						
+							
+							
+								
+									USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->cardWrapperParent
+									FIELD:sDEF.tiling.enable:=:0
+								
+							
+							
+								check
+							
+						
 					
 					
-						
-						
-							
-								USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->cardWrapperParent
-								FIELD:sDEF.multiImage.enable:=:0
-							
-						
-						
-							check
-						
+						
+							
+							
+								
+									USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->cardWrapperParent
+									FIELD:sDEF.multiImage.enable:=:0
+									FIELD:sDEF.tiling.enable:=:0
+								
+							
+							
+								check
+							
+						
 					
 					
-						
-						
-							
-								USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->cardWrapperParent
-								FIELD:sDEF.multiImage.enable:=:0
-							
-						
-						
-							check
-						
+						
+							
+							
+								
+									USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->cardWrapperParent
+									FIELD:sDEF.multiImage.enable:=:0
+									FIELD:sDEF.tiling.enable:=:0
+								
+							
+							
+								check
+							
+						
 					
 					
-						
-						Frontside with image - Backside with text (if "Text Top" available) or image (second image)
-						
-							
-								USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->cardWrapperParent
-								FIELD:sDEF.multiImage.enable:=:0
-							
-						
-						reload
-						
-							check
-						
+						
+							
+							Frontside with image - Backside with text (if "Text Top" available) or image (second image)
+							
+								
+									USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->cardWrapperParent
+									FIELD:sDEF.multiImage.enable:=:0
+									FIELD:sDEF.tiling.enable:=:0
+								
+							
+							reload
+							
+								check
+							
+						
 					
 					
-						
-						FIELD:flipcard:=:1
-						
-							select
-							selectSingle
-							
-								
-									180deg
-									180deg
-								
-								
-									540deg
-									540deg
-								
-								
-									900deg
-									900deg
-								
-							
-							180deg
-						
+						
+							
+							FIELD:flipcard:=:1
+							
+								select
+								selectSingle
+								
+									
+										180deg
+										180deg
+									
+									
+										540deg
+										540deg
+									
+									
+										900deg
+										900deg
+									
+								
+								180deg
+							
+						
 					
 					
-						
-						
-							check
-						
+						
+							
+							FIELD:sDEF.tiling.enable:=:0
+							
+								check
+							
+						
 					
 					
-						reload
-						
-						
-							check
-						
+						
+							reload
+							FIELD:sDEF.tiling.enable:=:0
+							
+							
+								check
+							
+						
 					
 					
-						
-						FIELD:button.enable:=:1
-						
-							check
-						
+						
+							
+							FIELD:button.enable:=:1
+							
+								check
+							
+						
 					
 					
-						
-						FIELD:button.enable:=:1
-						
-							select
-							selectSingle
-							
-								
-									Secondary
-									secondary
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:primary
-									primary
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:success
-									success
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:info
-									info
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:warning
-									warning
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:danger
-									danger
-								
-								
-									Link
-									card-link
-								
-							
-							secondary
-						
+						
+							
+							FIELD:button.enable:=:1
+							
+								select
+								selectSingle
+								
+									
+										Secondary
+										secondary
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:primary
+										primary
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:success
+										success
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:info
+										info
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:warning
+										warning
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:danger
+										danger
+									
+									
+										Link
+										card-link
+									
+								
+								secondary
+							
+						
 					
 					
-						
-						FIELD:button.enable:=:1
-						
-							input
-							
-							trim
-							48
-							99
-							
-						
+						
+							
+							FIELD:button.enable:=:1
+							
+								input
+								
+								trim
+								48
+								99
+								
+							
+						
 					
 					
-						
-						FIELD:button.enable:=:1
-						
-							check
-						
+						
+							
+							FIELD:button.enable:=:1
+							
+								check
+							
+						
 					
 					
-						
-						FIELD:button.enable:=:1
-						
-							check
-						
+						
+							
+							FIELD:button.enable:=:1
+							
+								check
+							
+						
 					
 					
-						
-						FIELD:button.enable:=:1
-						
-							check
-						
+						
+							
+							FIELD:button.enable:=:1
+							
+								check
+							
+						
 					
 					
-						
-						reload
-						
-							check
-						
+						
+							
+							FIELD:sDEF.tiling.enable:=:0
+							reload
+							
+								check
+							
+						
 					
 					
-						
-						FIELD:sDEF.cardborder:=:1
-						
-							select
-							selectSingle
-							
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:secondary
-									secondary
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:primary
-									primary
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:success
-									success
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:info
-									info
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:warning
-									warning
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:danger
-									danger
-								
-								
-									light
-									light
-								
-								
-									dark
-									dark
-								
-								
-									white
-									white
-								
-								
-									none
-									0
-								
-							
-							secondary
-						
+						
+							
+							FIELD:sDEF.cardborder:=:1
+							
+								select
+								selectSingle
+								
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:secondary
+										secondary
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:primary
+										primary
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:success
+										success
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:info
+										info
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:warning
+										warning
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:danger
+										danger
+									
+									
+										light
+										light
+									
+									
+										dark
+										dark
+									
+									
+										white
+										white
+									
+									
+										none
+										0
+									
+								
+								secondary
+							
+						
 					
 					
-						
-						
-							
-								USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->cardWrapperParent
-								FIELD:sDEF.multiImage.enable:=:0
-							
-						
-						
-							check
-						
+						
+							
+							
+								
+									FIELD:sDEF.multiImage.enable:=:0
+									FIELD:sDEF.tiling.enable:=:0
+								
+							
+							
+								check
+							
+						
 					
-					
-						
-						You need at least two images for the profile card. The second is the circular, smaller image
+
+					
+						
+						
+						Best in 2 columns with the following setting "col-lg-6 col-md-12 col-12"
 						reload
 						
 							check
 						
-					
-					
-						
-						FIELD:sDEF.multiImage.enable:=:1
-						
-							input
-							5
-							50
-							trim,int
-							
-								10
-								90
-							
-							
-								5
-								200
-							
-						
-					
-					
-						
-						FIELD:sDEF.multiImage.enable:=:1
+						
+					
+					
+						
+						
+						FIELD:sDEF.tiling.enable:=:1
 						
 							select
 							selectSingle
 							
 								
-									none
-									none
+									left
+									left
 								
 								
-									1
-									border border-1
-								
-								
-									2
-									border border-2
-								
-								
-									3
-									border border-3
-								
-								
-									4
-									border border-4
-								
-								
-									5
-									border border-5
+									right
+									right
 								
 							
-							border border-1
-							1
+							left							
 						
+						
+					
+					
+						
+							
+							You need at least two images for the profile card. The second is the circular, smaller image
+							
+								
+									FIELD:sDEF.horizontal:=:0
+									FIELD:sDEF.tiling.enable:=:0
+								
+							
+							reload
+							
+								check
+							
+						
+					
+					
+						
+							
+							FIELD:sDEF.multiImage.enable:=:1
+							
+								input
+								5
+								50
+								trim,int
+								
+									10
+									90
+								
+								
+									5
+									200
+								
+							
+						
+					
+					
+						
+							
+							FIELD:sDEF.multiImage.enable:=:1
+							
+								select
+								selectSingle
+								
+									
+										none
+										none
+									
+									
+										1
+										border border-1
+									
+									
+										2
+										border border-2
+									
+									
+										3
+										border border-3
+									
+									
+										4
+										border border-4
+									
+									
+										5
+										border border-5
+									
+								
+								border border-1
+								1
+							
+						
 					
 					
-						
-						FIELD:sDEF.multiImage.enable:=:1
-						
-							select
-							selectSingle
-							
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:secondary
-									secondary
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:primary
-									primary
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:success
-									success
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:info
-									info
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:warning
-									warning
-								
-								
-									LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:danger
-									danger
-								
-								
-									light
-									light
-								
-								
-									dark
-									dark
-								
-								
-									white
-									white
-								
-								
-									none
-									0
-								
-							
-							white
-						
+						
+							
+							FIELD:sDEF.multiImage.enable:=:1
+							
+								select
+								selectSingle
+								
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:secondary
+										secondary
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:primary
+										primary
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:success
+										success
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:info
+										info
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:warning
+										warning
+									
+									
+										LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang_be.xlf:danger
+										danger
+									
+									
+										light
+										light
+									
+									
+										dark
+										dark
+									
+									
+										white
+										white
+									
+									
+										none
+										0
+									
+								
+								white
+							
+						
 					
 					
-						
-						FIELD:sDEF.multiImage.enable:=:1
-						
-							check
-							1
-						
+						
+							
+							FIELD:sDEF.multiImage.enable:=:1
+							
+								check
+								1
+							
+						
 					
 					
-						
-						FIELD:sDEF.multiImage.enable:=:1
-						reload
-						
-							check
-						
+						
+							
+							FIELD:sDEF.multiImage.enable:=:1
+							reload
+							
+								check
+							
+						
 					
 					
-						
-						 
-							
-								FIELD:sDEF.multiImage.enable:=:1
-								FIELD:sDEF.multiImage.diagonal:=:1
-							
-						 
-						
-							input
-							5
-							50
-							trim,int
-							
-								10
-								90
-							
-							
-								5
-								200
-							
-						
+						
+							
+							 
+								
+									FIELD:sDEF.multiImage.enable:=:1
+									FIELD:sDEF.multiImage.diagonal:=:1
+								
+							 
+							
+								input
+								5
+								50
+								trim,int
+								
+									10
+									90
+								
+								
+									5
+									200
+								
+							
+						
 					
 					
-						
-						FIELD:sDEF.multiImage.enable:=:1
-						reload
-						
-							check
-						
+						
+							
+							FIELD:sDEF.multiImage.enable:=:1
+							reload
+							
+								check
+							
+						
 					
 					
-						
-						
-							
-								FIELD:sDEF.multiImage.enable:=:1
-								FIELD:sDEF.multiImage.socialmedia.enable:=:1
-							
-						
-						
-							check
-						
+						
+							
+							
+								
+									FIELD:sDEF.multiImage.enable:=:1
+									FIELD:sDEF.multiImage.socialmedia.enable:=:1
+								
+							
+							
+								check
+							
+						
 					
 					
-						
-						
-							
-								FIELD:sDEF.multiImage.enable:=:1
-								FIELD:sDEF.multiImage.socialmedia.enable:=:1
-							
-						
-						
-							input
-							inputLink
-						
+						
+							
+							
+								
+									FIELD:sDEF.multiImage.enable:=:1
+									FIELD:sDEF.multiImage.socialmedia.enable:=:1
+								
+							
+							
+								input
+								inputLink
+							
+						
 					
 					
-						
-						
-							
-								FIELD:sDEF.multiImage.enable:=:1
-								FIELD:sDEF.multiImage.socialmedia.enable:=:1
-							
-						
-						
-							input
-							inputLink
-						
+						
+							
+							
+								
+									FIELD:sDEF.multiImage.enable:=:1
+									FIELD:sDEF.multiImage.socialmedia.enable:=:1
+								
+							
+							
+								input
+								inputLink
+							
+						
 					
 					
-						
-						
-							
-								FIELD:sDEF.multiImage.enable:=:1
-								FIELD:sDEF.multiImage.socialmedia.enable:=:1
-							
-						
-						
-							input
-							inputLink
-						
+						
+							
+							
+								
+									FIELD:sDEF.multiImage.enable:=:1
+									FIELD:sDEF.multiImage.socialmedia.enable:=:1
+								
+							
+							
+								input
+								inputLink
+							
+						
 					
 					
-						
-						
-							
-								FIELD:sDEF.multiImage.enable:=:1
-								FIELD:sDEF.multiImage.socialmedia.enable:=:1
-							
-						
-						
-							input
-							inputLink
-						
+						
+							
+							
+								
+									FIELD:sDEF.multiImage.enable:=:1
+									FIELD:sDEF.multiImage.socialmedia.enable:=:1
+								
+							
+							
+								input
+								inputLink
+							
+						
 					
 					
-						
-						
-							
-								FIELD:sDEF.multiImage.enable:=:1
-								FIELD:sDEF.multiImage.socialmedia.enable:=:1
-							
-						
-						
-							input
-							inputLink
-						
+						
+							
+							
+								
+									FIELD:sDEF.multiImage.enable:=:1
+									FIELD:sDEF.multiImage.socialmedia.enable:=:1
+								
+							
+							
+								input
+								inputLink
+							
+						
 					
 					
-						
-						
-							
-								FIELD:sDEF.multiImage.enable:=:1
-								FIELD:sDEF.multiImage.socialmedia.enable:=:1
-							
-						
-						
-							input
-							inputLink
-						
+						
+							
+							
+								
+									FIELD:sDEF.multiImage.enable:=:1
+									FIELD:sDEF.multiImage.socialmedia.enable:=:1
+								
+							
+							
+								input
+								inputLink
+							
+						
 					
 					
-						
-						
-							
-								FIELD:sDEF.multiImage.enable:=:1
-								FIELD:sDEF.multiImage.socialmedia.enable:=:1
-							
-						
-						
-							input
-							inputLink
-						
+						
+							
+							
+								
+									FIELD:sDEF.multiImage.enable:=:1
+									FIELD:sDEF.multiImage.socialmedia.enable:=:1
+								
+							
+							
+								input
+								inputLink
+							
+						
 					
 					
-						
-						
-							
-								FIELD:sDEF.multiImage.enable:=:1
-								FIELD:sDEF.multiImage.socialmedia.enable:=:1
-							
-						
-						
-							input
-							inputLink
-						
+						
+							
+							
+								
+									FIELD:sDEF.multiImage.enable:=:1
+									FIELD:sDEF.multiImage.socialmedia.enable:=:1
+								
+							
+							
+								input
+								inputLink
+							
+						
 					
 				
 			
 		
 		
 			
-				Auto-layout (if parent wrapper)
+				array
+				
+					Auto-layout (if parent wrapper)
+				
 				array
 				
 					
-						
-						reload
-						USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
-						
-							select
-							selectSingle
-							
-								
-									no .col
-									none
-								
-								
-									Equal-width
-									equal
-								
-								
-									Column width
-									column
-								
-								
-									Variable width content
-									variable
-								
-							
-							equal
-							1
-						
+						
+							
+							reload
+							USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
+							
+								select
+								selectSingle
+								
+									
+										no .col
+										none
+									
+									
+										Equal-width
+										equal
+									
+									
+										Column width
+										column
+									
+									
+										Variable width content
+										variable
+									
+								
+								equal
+								1
+							
+						
 					
 					
-						
-						USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
-						
-							check
-						
+						
+							
+							USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
+							
+								check
+							
+						
 					
 					
-						
-						 
-								 
-								USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
-								USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->flexContainerParent
-								 
-						 
-						
-							select
-							selectSingle
-							
-								
-									none
-									
-								
-								
-									start
-									start
-								
-								
-									end
-									end
-								
-								
-									center
-									center
-								
-							
-							
-						
-					
-					
-						
+						
+							
 							 
 								  
 									USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
 									USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->flexContainerParent
 								  
 							 
-						
-							select
-							selectSingle
-							
-								
-									none
-									
-								
-								
-									xs
-									xs
-								
-								
-									sm
-									sm
-								
-								
-									md
-									md
-								
-								
-									lg
-									lg
-								
-								
-									xl
-									xl
-								
+							
+								select
+								selectSingle
+								
+									
+										none
+										
+									
+									
+										start
+										start
+									
+									
+										end
+										end
+									
+									
+										center
+										center
+									
+								
+								
+							
+						
+					
+					
+						
+							
+								 
+								   
+										USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
+										USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->flexContainerParent
+								   
+								 
+							
+								select
+								selectSingle
+								
+									
+										none
+										
+									
+									
+										xs
+										xs
+									
+									
+										sm
+										sm
+									
+									
+										md
+										md
+									
+									
+										lg
+										lg
+									
+									
+										xl
+										xl
+									
 
-							
-							
-							1
-						
+								
+								
+								1
+							
+						
 					
 					
-						
-						USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->flexContainerParent
-						
-							select
-							selectSingle
-							
-								
-									none
-									
-								
-								
-									right
-									mr
-								
-								
-									left
-									ml
-								
-								
-									top
-									mt
-								
-								
-									bottom
-									mb
-								
-							
-							
-						
+						
+							
+							USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->flexContainerParent
+							
+								select
+								selectSingle
+								
+									
+										none
+										
+									
+									
+										right
+										mr
+									
+									
+										left
+										ml
+									
+									
+										top
+										mt
+									
+									
+										bottom
+										mb
+									
+								
+								
+							
+						
 					
 					
-						
-						USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->flexContainerParent
-						
-							select
-							selectSingle
-							
-								
-									none
-									
-								
-								
-									1
-									1
-								
-								
-									2
-									2
-								
-								
-									3
-									3
-								
-								
-									4
-									4
-								
-								
-									5
-									5
-								
-								
-									6
-									6
-								
-								
-									7
-									7
-								
-								
-									8
-									8
-								
-								
-									9
-									9
-								
-								
-									10
-									10
-								
-								
-									11
-									11
-								
-								
-									12
-									12
-								
-							
-							
-							1
-						
+						
+							
+							USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->flexContainerParent
+							
+								select
+								selectSingle
+								
+									
+										none
+										
+									
+									
+										1
+										1
+									
+									
+										2
+										2
+									
+									
+										3
+										3
+									
+									
+										4
+										4
+									
+									
+										5
+										5
+									
+									
+										6
+										6
+									
+									
+										7
+										7
+									
+									
+										8
+										8
+									
+									
+										9
+										9
+									
+									
+										10
+										10
+									
+									
+										11
+										11
+									
+									
+										12
+										12
+									
+								
+								
+								1
+							
+						
 					
+
 				
 			
 		
 		
 			
-				col-*
+				array
+				
+					col-*
+				
 				array
 				
 					
-						
-							 
-								  
-									USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
-								  
-									FIELD:auto.gridSystem:=:column
-									FIELD:auto.gridSystem:=:variable
-								  
-								  
-							 
-						
-							select
-							selectSingle
-							
-								
-									none
-									0
-								
-								
-									1
-									1
-								
-								
-									2
-									2
-								
-								
-									3
-									3
-								
-								
-									4
-									4
-								
-								
-									5
-									5
-								
-								
-									6
-									6
-								
-								
-									7
-									7
-								
-								
-									8
-									8
-								
-								
-									9
-									9
-								
-								
-									10
-									10
-								
-								
-									11
-									11
-								
-								
-									12
-									12
-								
-								
-									auto
-									auto
-								
-								
-									equal
-									equal
-								
-							
-							0
-							1
-						
+						
+							
+								 
+								   
+										USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
+								   
+										FIELD:auto.gridSystem:=:column
+										FIELD:auto.gridSystem:=:variable
+								   
+								   
+								 
+							
+								select
+								selectSingle
+								
+									
+										none
+										0
+									
+									
+										1
+										1
+									
+									
+										2
+										2
+									
+									
+										3
+										3
+									
+									
+										4
+										4
+									
+									
+										5
+										5
+									
+									
+										6
+										6
+									
+									
+										7
+										7
+									
+									
+										8
+										8
+									
+									
+										9
+										9
+									
+									
+										10
+										10
+									
+									
+										11
+										11
+									
+									
+										12
+										12
+									
+									
+										auto
+										auto
+									
+									
+										equal
+										equal
+									
+								
+								0
+								1
+							
+						
 					
 				
 			
 		
 		
 			
-				col-sm-*
+				array
+				
+					col-sm-*
+				
 				array
 				
 					
-						
-							 
-								  
-									USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
-									FIELD:auto.gridSystem:=:variable
-								  
-							 
-						
-							select
-							selectSingle
-							
-								
-									none
-									0
-								
-								
-									1
-									1
-								
-								
-									2
-									2
-								
-								
-									3
-									3
-								
-								
-									4
-									4
-								
-								
-									5
-									5
-								
-								
-									6
-									6
-								
-								
-									7
-									7
-								
-								
-									8
-									8
-								
-								
-									9
-									9
-								
-								
-									10
-									10
-								
-								
-									11
-									11
-								
-								
-									12
-									12
-								
-								
-									auto
-									auto
-								
-								
-									equal
-									equal
-								
-							
-							0
-							1
-						
+						
+							
+								 
+								   
+										USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
+										FIELD:auto.gridSystem:=:variable
+								   
+								 
+							
+								select
+								selectSingle
+								
+									
+										none
+										0
+									
+									
+										1
+										1
+									
+									
+										2
+										2
+									
+									
+										3
+										3
+									
+									
+										4
+										4
+									
+									
+										5
+										5
+									
+									
+										6
+										6
+									
+									
+										7
+										7
+									
+									
+										8
+										8
+									
+									
+										9
+										9
+									
+									
+										10
+										10
+									
+									
+										11
+										11
+									
+									
+										12
+										12
+									
+									
+										auto
+										auto
+									
+									
+										equal
+										equal
+									
+								
+								0
+								1
+							
+						
 					
 				
 			
 		
 		
 			
-				col-md-*
+				
+					col-md-*
+				
 				array
 				
 					
-						
-							 
-								  
-									USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
-									FIELD:auto.gridSystem:=:variable
-								  
-							 
-						
-							select
-							selectSingle
-							
-								
-									none
-									0
-								
-								
-									1
-									1
-								
-								
-									2
-									2
-								
-								
-									3
-									3
-								
-								
-									4
-									4
-								
-								
-									5
-									5
-								
-								
-									6
-									6
-								
-								
-									7
-									7
-								
-								
-									8
-									8
-								
-								
-									9
-									9
-								
-								
-									10
-									10
-								
-								
-									11
-									11
-								
-								
-									12
-									12
-								
-								
-									auto
-									auto
-								
-								
-									equal
-									equal
-								
-							
-							0
-							1
-						
+						
+							
+								 
+								   
+										USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
+										FIELD:auto.gridSystem:=:variable
+								   
+								 
+							
+								select
+								selectSingle
+								
+									
+										none
+										0
+									
+									
+										1
+										1
+									
+									
+										2
+										2
+									
+									
+										3
+										3
+									
+									
+										4
+										4
+									
+									
+										5
+										5
+									
+									
+										6
+										6
+									
+									
+										7
+										7
+									
+									
+										8
+										8
+									
+									
+										9
+										9
+									
+									
+										10
+										10
+									
+									
+										11
+										11
+									
+									
+										12
+										12
+									
+									
+										auto
+										auto
+									
+									
+										equal
+										equal
+									
+								
+								0
+								1
+							
+						
 					
+
 				
 			
 		
 		
 			
-				col-lg-*
+				array
+				
+					col-lg-*
+				
 				array
 				
 					
-						
-							 
-								  
-									USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
-									FIELD:auto.gridSystem:=:variable
-								  
-							 
-						
-							select
-							selectSingle
-							
-								
-									none
-									0
-								
-								
-									1
-									1
-								
-								
-									2
-									2
-								
-								
-									3
-									3
-								
-								
-									4
-									4
-								
-								
-									5
-									5
-								
-								
-									6
-									6
-								
-								
-									7
-									7
-								
-								
-									8
-									8
-								
-								
-									9
-									9
-								
-								
-									10
-									10
-								
-								
-									11
-									11
-								
-								
-									12
-									12
-								
-								
-									auto
-									auto
-								
-								
-									equal
-									equal
-								
-							
-							0
-							1
-						
+						
+							
+								 
+								   
+										USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
+										FIELD:auto.gridSystem:=:variable
+								   
+								 
+							
+								select
+								selectSingle
+								
+									
+										none
+										0
+									
+									
+										1
+										1
+									
+									
+										2
+										2
+									
+									
+										3
+										3
+									
+									
+										4
+										4
+									
+									
+										5
+										5
+									
+									
+										6
+										6
+									
+									
+										7
+										7
+									
+									
+										8
+										8
+									
+									
+										9
+										9
+									
+									
+										10
+										10
+									
+									
+										11
+										11
+									
+									
+										12
+										12
+									
+									
+										auto
+										auto
+									
+									
+										equal
+										equal
+									
+								
+								0
+								1
+							
+						
 					
+
 				
 			
 		
 		
 			
-				col-xl-*
+				array
+				
+					col-xl-*
+				
 				array
 				
+
 					
-						
-							 
-								  
-									USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
-									FIELD:auto.gridSystem:=:variable
-								  
-							 
-						
-							select
-							selectSingle
-							
-								
-									none
-									0
-								
-								
-									1
-									1
-								
-								
-									2
-									2
-								
-								
-									3
-									3
-								
-								
-									4
-									4
-								
-								
-									5
-									5
-								
-								
-									6
-									6
-								
-								
-									7
-									7
-								
-								
-									8
-									8
-								
-								
-									9
-									9
-								
-								
-									10
-									10
-								
-								
-									11
-									11
-								
-								
-									12
-									12
-								
-								
-									auto
-									auto
-								
-								
-									equal
-									equal
-								
-							
-							0
-							1
-						
+						
+							
+								 
+								   
+										USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
+										FIELD:auto.gridSystem:=:variable
+								   
+								 
+							
+								select
+								selectSingle
+								
+									
+										none
+										0
+									
+									
+										1
+										1
+									
+									
+										2
+										2
+									
+									
+										3
+										3
+									
+									
+										4
+										4
+									
+									
+										5
+										5
+									
+									
+										6
+										6
+									
+									
+										7
+										7
+									
+									
+										8
+										8
+									
+									
+										9
+										9
+									
+									
+										10
+										10
+									
+									
+										11
+										11
+									
+									
+										12
+										12
+									
+									
+										auto
+										auto
+									
+									
+										equal
+										equal
+									
+								
+								0
+								1
+							
+						
 					
 				
 			
 		
 		
 			
-				col-xxl-*
+				array
+				
+					col-xxl-*
+				
 				array
 				
 					
-						
-							 
-								  
-									USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
-									FIELD:auto.gridSystem:=:variable
-								  
-							 
-						
-							select
-							selectSingle
-							
-								
-									none
-									0
-								
-								
-									1
-									1
-								
-								
-									2
-									2
-								
-								
-									3
-									3
-								
-								
-									4
-									4
-								
-								
-									5
-									5
-								
-								
-									6
-									6
-								
-								
-									7
-									7
-								
-								
-									8
-									8
-								
-								
-									9
-									9
-								
-								
-									10
-									10
-								
-								
-									11
-									11
-								
-								
-									12
-									12
-								
-								
-									auto
-									auto
-								
-								
-									equal
-									equal
-								
-							
-							0
-							1
-						
+						
+							
+								 
+								   
+										USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->autoLayoutParent
+										FIELD:auto.gridSystem:=:variable
+								   
+								 
+							
+								select
+								selectSingle
+								
+									
+										none
+										0
+									
+									
+										1
+										1
+									
+									
+										2
+										2
+									
+									
+										3
+										3
+									
+									
+										4
+										4
+									
+									
+										5
+										5
+									
+									
+										6
+										6
+									
+									
+										7
+										7
+									
+									
+										8
+										8
+									
+									
+										9
+										9
+									
+									
+										10
+										10
+									
+									
+										11
+										11
+									
+									
+										12
+										12
+									
+									
+										auto
+										auto
+									
+									
+										equal
+										equal
+									
+								
+								0
+								1
+							
+						
 					
 				
 			
 		
 	
-
+
\ No newline at end of file
diff --git a/Configuration/FlexForms/Container/BackgroundWrapper.xml b/Configuration/FlexForms/Container/BackgroundWrapper.xml
index 5494bfce..b4fd1f34 100644
--- a/Configuration/FlexForms/Container/BackgroundWrapper.xml
+++ b/Configuration/FlexForms/Container/BackgroundWrapper.xml
@@ -87,11 +87,10 @@
 						
 						USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->isImage
 						
-							input
+							number
 							4
 							4
 							1296
-							int
 							0
 						
 					
@@ -136,9 +135,8 @@
 							
 						
 						
-							input
+							number
 							
-							int
 							6
 							60
 						
@@ -201,9 +199,8 @@
 							
 						
 						
-							input
+							number
 							
-							int
 							6
 							1
 						
@@ -212,14 +209,13 @@
 						
 						USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->isImage
 						
-							input
+							number
 							5
-							0
-							num
 							
 								0
 								100
 							
+							0
 							
 								1
 								200
@@ -230,10 +226,9 @@
 						
 						USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->isImage
 						
-							input
+							number
 							5
 							0
-							num
 							
 								0
 								100
@@ -248,10 +243,9 @@
 						
 						USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->isImage
 						
-							input
+							number
 							5
 							100
-							num
 							
 								0
 								100
@@ -283,9 +277,8 @@
 						
 						USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->isNoMedia
 						
-							input
+							number
 							
-							int
 							6
 							10
 						
@@ -399,10 +392,9 @@
 						USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->isLocalVideo
 						e.g.: 100 or -100
 						
-							input
+							number
 							5
 							0
-							int
 							
 								-100
 								100
@@ -419,10 +411,9 @@
 						USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->isLocalVideo
 						e.g.: 100 or -100
 						
-							input
+							number
 							5
 							0
-							int
 							
 								-200
 								200
@@ -439,10 +430,9 @@
 						 card-img-overlay min-height in px
 						 USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->isLocalVideo
 						 
-							input
+							number
 							5
 							200
-							int
 							
 								100
 								1000
@@ -459,10 +449,9 @@
 						 figure.video width in %
 						 USER:T3SBS\T3sbootstrap\UserFunction\TcaMatcher->isLocalVideo
 						 
-							input
+							number
 							5
 							100
-							int
 							
 								100
 								500
@@ -532,10 +521,9 @@
 							
 						
 						
-							input
+							number
 							5
 							300
-							int+
 						
 					
 					
@@ -548,10 +536,9 @@
 						
 						e.g.: -100 or 100 (default = 50)
 						
-							input
+							number
 							5
 							50
-							int
 							
 								-50
 								150
diff --git a/Configuration/FlexForms/Container/CardWrapper.xml b/Configuration/FlexForms/Container/CardWrapper.xml
index ae1061c8..6eede781 100644
--- a/Configuration/FlexForms/Container/CardWrapper.xml
+++ b/Configuration/FlexForms/Container/CardWrapper.xml
@@ -61,7 +61,7 @@
 
 					
 					
-						
+						
 						reload
 						
 							select
@@ -201,23 +201,33 @@
 							0
 						
 					
-
 					
 						
+						slides visible at the same time on slider's container (can be empty if you use breakpoints)
 						FIELD:sDEF.card_wrapper:=:slider
 						
-							input
+							number
 							4
-							4
+							0
 							0
 						
 					
-
+					
+						
+						breakpoints allows to set different parameter for different responsive breakpoints (screen sizes)
+						FIELD:sDEF.card_wrapper:=:slider
+						
+							number
+							4
+							1
+							0
+						
+					
 					
 						
 						FIELD:sDEF.card_wrapper:=:slider
 						
-							input
+							number
 							4
 							2
 							0
@@ -227,7 +237,7 @@
 						
 						FIELD:sDEF.card_wrapper:=:slider
 						
-							input
+							number
 							4
 							3
 							0
@@ -237,44 +247,44 @@
 						
 						FIELD:sDEF.card_wrapper:=:slider
 						
-							input
+							number
 							4
 							4
 							0
 						
 					
-
 					
 						
+						Set numbers of slides to define and enable group sliding
 						FIELD:sDEF.card_wrapper:=:slider
 						
-							input
+							number
+							
+								1
+							
 							4
 							1
 							0
 						
 					
-
 					
 						
 						FIELD:sDEF.card_wrapper:=:slider
 						
-							input
+							number
 							4
 							20
 							0
 						
 					
-
 					
 						
-						Amount of slides in loop mode should be at least 2x of "slidesPerView" value.
+						amount of slides in loop mode should be at least 2x of "slidesPerView" value.
 						FIELD:sDEF.card_wrapper:=:slider
 						
 							check
 						
 					
-
 					
 						
 						FIELD:sDEF.card_wrapper:=:slider
@@ -282,7 +292,6 @@
 							check
 						
 					
-
 					
 						
 						FIELD:sDEF.card_wrapper:=:slider
@@ -290,7 +299,6 @@
 							check
 						
 					
-
 					
 						
 						FIELD:sDEF.card_wrapper:=:slider
@@ -299,7 +307,6 @@
 							check
 						
 					
-
 					
 						
 						
@@ -309,22 +316,19 @@
 							
 						
 						
-							input
+							number
 							4
 							5000
 							0
 						
 					
-
 					
 						
 						
-							input
+							number
 							5
-							num
 						
 					
-
 					
 						
 						FIELD:sDEF.card_wrapper:=:deck
diff --git a/Configuration/FlexForms/Container/SwiperContainer.xml b/Configuration/FlexForms/Container/SwiperContainer.xml
index 43b20e20..3e32312f 100644
--- a/Configuration/FlexForms/Container/SwiperContainer.xml
+++ b/Configuration/FlexForms/Container/SwiperContainer.xml
@@ -110,19 +110,46 @@
 					
 					
 						
-						FIELD:general.sliderStyle:=:Default
+						slides visible at the same time on slider's container (can be empty if you use breakpoints)
+						
+							
+								FIELD:general.sliderStyle:=:Default
+								FIELD:general.useCustomSwiperJs:=:0
+							
+						
 						
-							input
+							number
 							4
 							4
 							0
 						
 					
+					
+						
+						breakpoints allows to set different parameter for different responsive breakpoints (screen sizes)
+						
+							
+								FIELD:general.sliderStyle:=:Default
+								FIELD:general.useCustomSwiperJs:=:0
+							
+						
+						
+							number
+							4
+							1
+							0
+						
+					
 					
 						
-						FIELD:general.sliderStyle:=:Default
+						
+							
+								FIELD:general.sliderStyle:=:Default
+								FIELD:general.useCustomSwiperJs:=:0
+							
+						
 						
-							input
+							number
 							4
 							2
 							0
@@ -130,9 +157,14 @@
 					
 					
 						
-						FIELD:general.sliderStyle:=:Default
+						
+							
+								FIELD:general.sliderStyle:=:Default
+								FIELD:general.useCustomSwiperJs:=:0
+							
+						
 						
-							input
+							number
 							4
 							3
 							0
@@ -140,9 +172,14 @@
 					
 					
 						
-						FIELD:general.sliderStyle:=:Default
+						
+							
+								FIELD:general.sliderStyle:=:Default
+								FIELD:general.useCustomSwiperJs:=:0
+							
+						
 						
-							input
+							number
 							4
 							4
 							0
@@ -150,9 +187,18 @@
 					
 					
 						
-						FIELD:general.sliderStyle:=:Default
+						set numbers of slides to define and enable group sliding
+						
+							
+								FIELD:general.sliderStyle:=:Default
+								FIELD:general.useCustomSwiperJs:=:0
+							
+						
 						
-							input
+							number
+							
+								1
+							
 							4
 							1
 							0
@@ -160,9 +206,14 @@
 					
 					
 						
-						FIELD:general.sliderStyle:=:Default
+						
+							
+								FIELD:general.sliderStyle:=:Default
+								FIELD:general.useCustomSwiperJs:=:0
+							
+						
 						
-							input
+							number
 							4
 							20
 							0
@@ -170,29 +221,49 @@
 					
 					
 						
-						Amount of slides in loop mode should be at least 2x of slidesPerView value.
-						FIELD:general.sliderStyle:=:Default
+						amount of slides in loop mode should be at least 2x of slidesPerView value.
+						
+							
+								FIELD:general.sliderStyle:=:Default
+								FIELD:general.useCustomSwiperJs:=:0
+							
+						
 						
 							check
 						
 					
 					
 						
-						FIELD:general.sliderStyle:=:Default
+						
+							
+								FIELD:general.sliderStyle:=:Default
+								FIELD:general.useCustomSwiperJs:=:0
+							
+						
 						
 							check
 						
 					
 					
 						
-						FIELD:general.sliderStyle:=:Default
+						
+							
+								FIELD:general.sliderStyle:=:Default
+								FIELD:general.useCustomSwiperJs:=:0
+							
+						
 						
 							check
 						
 					
 					
 						
-						FIELD:general.sliderStyle:=:Default
+						
+							
+								FIELD:general.sliderStyle:=:Default
+								FIELD:general.useCustomSwiperJs:=:0
+							
+						
 						reload
 						
 							check
@@ -204,10 +275,11 @@
 							
 								FIELD:general.sliderStyle:=:Default
 								FIELD:general.autoplay:=:1
+								FIELD:general.useCustomSwiperJs:=:0
 							
 						
 						
-							input
+							number
 							4
 							5000
 							0
@@ -235,11 +307,36 @@
 					
 					
 						
-						Not working with a link inside the carousel content.
+						not working with a link inside the carousel content.
 						
 							check
 						
 					
+					
+						
+						use your own Swiper JS
+						FIELD:general.sliderStyle:=:Default
+						reload
+						
+							check
+						
+					
+					
+						
+						overwrites all the above settings
+						
+							
+								FIELD:general.sliderStyle:=:Default
+								FIELD:general.useCustomSwiperJs:=:1
+							
+						
+						
+							text
+							500
+							9999
+							
+						
+					
 				
 			
 		
diff --git a/Configuration/RTE/Default.yaml b/Configuration/RTE/Default.yaml
index 0e3df8cf..6a9ada0c 100644
--- a/Configuration/RTE/Default.yaml
+++ b/Configuration/RTE/Default.yaml
@@ -123,9 +123,15 @@ editor:
 #        - { name: 'Button Info', element: 'a', 'classes': ['btn btn-info'] } 
 #        - { name: 'Button Light', element: 'a', 'classes': ['btn btn-light'] } 
 #        - { name: 'Button Dark', element: 'a', 'classes': ['btn btn-dark'] } 
+
+
+
+
+
+
         
         # Textstile - Badges
-        - { name: 'Badge Primary', element: 'span', 'classes': ['badge text-bg-primary'] } 
+        - { name: 'Badge Primary', element: 'span', 'classes': ['badge', 'text-bg-primary'] } 
         - { name: 'Badge Secondary', element: 'span', 'classes': ['badge text-bg-secondary'] } 
         - { name: 'Badge Success', element: 'span', 'classes': ['badge text-bg-success'] } 
         - { name: 'Badge Danger', element: 'span', 'classes': ['badge text-bg-danger'] } 
diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml
index 105bedf8..62c307a5 100644
--- a/Configuration/Services.yaml
+++ b/Configuration/Services.yaml
@@ -34,10 +34,8 @@ services:
     tags:
       - name: event.listener
         identifier: 't3sbootstrap/newsFlexParsing'
-        event: TYPO3\CMS\Core\Configuration\Event\AfterFlexFormDataStructureParsedEvent
 
   T3SBS\T3sbootstrap\Backend\EventListener\FlexForm\FlexformEvent:
     tags:
       - name: event.listener
         identifier: 't3sbootstrap/flexParsing'
-        event: TYPO3\CMS\Core\Configuration\Event\AfterFlexFormDataStructureParsedEvent
diff --git a/Configuration/TCA/Overrides/sys_file_reference.php b/Configuration/TCA/Overrides/sys_file_reference.php
index adf72358..edcd96e8 100644
--- a/Configuration/TCA/Overrides/sys_file_reference.php
+++ b/Configuration/TCA/Overrides/sys_file_reference.php
@@ -283,14 +283,32 @@
 			]
 		],
 	],
-
+	'tx_t3sbootstrap_video_ratio' => [
+		'exclude' => 1,
+		'label' => 'Custom aspect ratio (default: 16:9)',
+		'description' => 'you can use any aspect ratio - e.g.: 4:3 (textmedia only)',
+		'displayCond' => 'FIELD:tablenames:=:tt_content',
+		'config' => [
+			'type' => 'input',
+			'size' => 10,
+			'eval' => 'trim',
+			'valuePicker' => [
+				'items' => [
+					['16:9 (widescreen)', '16:9'],
+					['9:16 (vertical)', '9:16'],
+					['1:1 (square)', '1:1'],
+					['4:3 (fullscreen)', '4:3'],
+					['21:9 (cinematic widescreen)', '21:9']
+				],
+			],
+			'default' => '16:9'
+		],
+	],	
 ];
 
-
 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns('sys_file_reference',$tempSysFileReferenceColumns);
 unset($tempSysFileReferenceColumns);
 
-
 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addFieldsToPalette('sys_file_reference', 'imageoverlayPalette','--linebreak--,tx_t3sbootstrap_description_align','after:title');
 
 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addFieldsToPalette('sys_file_reference', 'imageoverlayPalette','--linebreak--,tx_t3sbootstrap_extra_class','after:tx_t3sbootstrap_description_align');
@@ -320,6 +338,7 @@
 }
 
 if (array_key_exists('ratio', $extconf) && $extconf['ratio']) {
+	\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addFieldsToPalette('sys_file_reference', 'videoOverlayPalette','--linebreak--,tx_t3sbootstrap_video_ratio','after:autoplay');
 	\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addFieldsToPalette('sys_file_reference', 'imageoverlayPalette','--linebreak--,tx_t3sbootstrap_shift_vertical','after:tx_t3sbootstrap_description_align');
 	\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addFieldsToPalette('sys_file_reference', 'imageoverlayPalette','--linebreak--,tx_t3sbootstrap_shift_horizontal','after:tx_t3sbootstrap_shift_vertical');
 }
\ No newline at end of file
diff --git a/Configuration/TSConfig/BackendLayouts/Default/BootstrapCondition.tsconfig b/Configuration/TSConfig/BackendLayouts/Default/BootstrapCondition.tsconfig
index 1adb2841..53750fd1 100644
--- a/Configuration/TSConfig/BackendLayouts/Default/BootstrapCondition.tsconfig
+++ b/Configuration/TSConfig/BackendLayouts/Default/BootstrapCondition.tsconfig
@@ -2,16 +2,16 @@
 [colPosList('Content')]
 # Content only (no Jumbotron = 3 & no Footer = 4)
 mod.SHARED.colPos_list = 0,1,2
-[END]
+[global]
 [colPosList('Jumbotron')]
 # Content & Jumbotron
 mod.SHARED.colPos_list = 3,0,1,2
-[END]
+[global]
 [colPosList('Footer')]
 # Content & Footer
 mod.SHARED.colPos_list = 0,1,2,4
-[END]
+[global]
 [colPosList('All')]
 # Content & Footer & Jumbotron
 mod.SHARED.colPos_list = 3,0,1,2,4
-[END]
+[global]
diff --git a/Configuration/TSConfig/BackendLayouts/Expanded/BootstrapCondition.tsconfig b/Configuration/TSConfig/BackendLayouts/Expanded/BootstrapCondition.tsconfig
index a2b6e109..e52fda79 100644
--- a/Configuration/TSConfig/BackendLayouts/Expanded/BootstrapCondition.tsconfig
+++ b/Configuration/TSConfig/BackendLayouts/Expanded/BootstrapCondition.tsconfig
@@ -2,64 +2,64 @@
 [colPosList('Content')]
 # Content only (no Jumbotron = 3 & no Footer = 4)
 mod.SHARED.colPos_list = 0,1,2
-[END]
+[global]
 [colPosList('Jumbotron')]
 # Content & Jumbotron
 mod.SHARED.colPos_list = 3,0,1,2
-[END]
+[global]
 [colPosList('Footer')]
 # Content & Footer
 mod.SHARED.colPos_list = 0,1,2,4
-[END]
+[global]
 [colPosList('All')]
 # Content & Footer & Jumbotron
 mod.SHARED.colPos_list = 3,0,1,2,4
-[END]
+[global]
 [colPosList('AllandTop')]
 # All & expanded Top
 mod.SHARED.colPos_list = 3,20,0,1,2,4
-[END]
+[global]
 [colPosList('AllandBottom')]
 # All & expanded Bottom
 mod.SHARED.colPos_list = 3,0,1,2,4,21
-[END]
+[global]
 [colPosList('JumbotronandTop')]
 # All & expanded Top
 mod.SHARED.colPos_list = 3,20,0,1,2
-[END]
+[global]
 [colPosList('JumbotronandBottom')]
 # All & expanded Bottom
 mod.SHARED.colPos_list = 3,0,1,2,21
-[END]
+[global]
 [colPosList('FooterandTop')]
 # All & expanded Top
 mod.SHARED.colPos_list = 20,0,1,2,4
-[END]
+[global]
 [colPosList('FooterandBottom')]
 # All & expanded Bottom
 mod.SHARED.colPos_list = 0,1,2,4,21
-[END]
+[global]
 [colPosList('ContentandTop')]
 # All & expanded Top
 mod.SHARED.colPos_list = 20,0,1,2
-[END]
+[global]
 [colPosList('ContentandBottom')]
 # All & expanded Bottom
 mod.SHARED.colPos_list = 0,1,2,21
-[END]
+[global]
 [colPosList('AllandTopBottom')]
 # All & expanded Top & Bottom
 mod.SHARED.colPos_list = 3,20,1,0,2,21,4
-[END]
+[global]
 [colPosList('JumbotronandTopBottom')]
 # All & expanded Top & Bottom
 mod.SHARED.colPos_list = 3,20,0,1,2,21
-[END]
+[global]
 [colPosList('FooterandTopBottom')]
 # All & expanded Top & Bottom
 mod.SHARED.colPos_list = 20,0,1,2,4,21
-[END]
+[global]
 [colPosList('ContentandTopBottom')]
 # All & expanded Top & Bottom
 mod.SHARED.colPos_list = 20,0,1,2,21
-[END]
+[global]
diff --git a/Configuration/TSConfig/Page.tsconfig b/Configuration/TSConfig/Page.tsconfig
index 1b25bd5c..6f926d87 100644
--- a/Configuration/TSConfig/Page.tsconfig
+++ b/Configuration/TSConfig/Page.tsconfig
@@ -6,12 +6,10 @@ TCAdefaults.pages.tx_t3sbootstrap_container = container
 [tree.level in [2]]
 [else]
 TCEFORM.pages.tx_t3sbootstrap_dropdownRight.disabled = 1
-[END]
+[global]
 
 [traverse(page, "doktype") == 198]
 TCEFORM.pages.subtitle.disabled = 1
 TCEFORM.pages.nav_title.disabled = 1
 TCEFORM.pages.crdate.disabled = 1
-[END]
-
-
+[global]
diff --git a/Configuration/TypoScript/Content/Content.typoscript b/Configuration/TypoScript/Content/Content.typoscript
index fbed6066..0aa9ab08 100644
--- a/Configuration/TypoScript/Content/Content.typoscript
+++ b/Configuration/TypoScript/Content/Content.typoscript
@@ -62,4 +62,4 @@ lib.content.footerFromPid {
 [loaded('indexed_search')]
 lib.dynamicContent.10.wrap = |
 lib.dynamicContentSlide.10.wrap = |
-[END]
+[global]
diff --git a/Configuration/TypoScript/Lib/ContentElement.typoscript b/Configuration/TypoScript/Lib/ContentElement.typoscript
index c07061aa..cacd1b02 100644
--- a/Configuration/TypoScript/Lib/ContentElement.typoscript
+++ b/Configuration/TypoScript/Lib/ContentElement.typoscript
@@ -144,7 +144,7 @@ lib.contentElement {
 
 [{$bootstrap.image.minimumWidth} == 1]
 lib.contentElement.settings.srcsetMobile = 575
-[END]
+[global]
 
 # 2 == halkabox - https://github.com/ahmednooor/halkaBox.js
 [{$bootstrap.config.lightboxSelection} == 2]
@@ -153,7 +153,7 @@ lib.contentElement.settings.media.popup {
 	linkParams.ATagParams.dataWrap = class="gallery-{file:current:uid_foreign}"  data-title="{file:current:description}"
 	directImageLink = 1
 }
-[END]
+[global]
 
 # 3 == LightBox Plugin - glightbox.js - https://github.com/biati-digital/glightbox
 [{$bootstrap.config.lightboxSelection} == 3]
@@ -162,9 +162,9 @@ lib.contentElement.settings.media.popup {
 	linkParams.ATagParams.dataWrap = id="ex-{file:current:uid}" class="glightbox" data-glightbox="title:{file:current:description}" data-gallery="gallery-{file:current:uid_foreign}" data-effect="fade"
 	directImageLink = 1
 }
-[END]
+[global]
 
 [{$bootstrap.ext.news} == 1]
 # settings for news lightbox
 plugin.tx_news.settings.media.popup < lib.contentElement.settings.media.popup
-[END]
+[global]
diff --git a/Configuration/TypoScript/Lib/_main.typoscript b/Configuration/TypoScript/Lib/_main.typoscript
index 66afc800..e6b7b758 100644
--- a/Configuration/TypoScript/Lib/_main.typoscript
+++ b/Configuration/TypoScript/Lib/_main.typoscript
@@ -124,5 +124,5 @@ lib.printButton {
 
 [siteLanguage("languageId") == "0"]
 lib.printButton.stdWrap.typolink.title = Druckversion dieser Seite in einem neuen Fenster öffnen
-[END]
+[global]
 
diff --git a/Configuration/TypoScript/Page/IncludeCdn.typoscript b/Configuration/TypoScript/Page/IncludeCdn.typoscript
index b6ebc645..8f144048 100644
--- a/Configuration/TypoScript/Page/IncludeCdn.typoscript
+++ b/Configuration/TypoScript/Page/IncludeCdn.typoscript
@@ -28,7 +28,6 @@ page {
 		jqueryCDN.forceOnTop = 1
 		jqueryCDN.if.isFalse = {$bootstrap.disable.jquery}
 
-
 		popperCDN = https://cdnjs.cloudflare.com/ajax/libs/popper.js/{$bootstrap.cdn.popperjs}/umd/popper.min.js
 		popperCDN.integrity = {$bootstrap.cdn.popperjsIntegrity}
 		popperCDN.external = 1
diff --git a/Configuration/TypoScript/Page/IncludeDefault.typoscript b/Configuration/TypoScript/Page/IncludeDefault.typoscript
index 51fc4d9f..b35a6ecf 100644
--- a/Configuration/TypoScript/Page/IncludeDefault.typoscript
+++ b/Configuration/TypoScript/Page/IncludeDefault.typoscript
@@ -20,9 +20,8 @@ page {
 
 		googleFontCSS = /fileadmin/T3SB/Resources/Public/CSS/googlefonts.css
 		googleFontCSS.forceOnTop = 1
-		googleFontCSS.if {
-			isTrue = {$bootstrap.cdn.googlefonts}
-		}
+		googleFontCSS.if.isTrue = {$bootstrap.cdn.googlefonts}
+
 	}
 
 	##########################################################################################################
@@ -94,12 +93,6 @@ page.includeJSLibs {
 [{$bootstrap.config.faLinkIcons} == 1 && {$bootstrap.extconf.fontawesomeCss} == 0]
 page.includeCSS.t3sfontawesomeMinLink = EXT:t3sbootstrap/Resources/Public/Contrib/Fontawesome/css/fontawesomeMinLink.css
 [global]
-#[{$bootstrap.config.faLinkIcons} == 1 && {$bootstrap.extconf.fontawesomeCss} == 1]
-#page.includeCSS.t3sfontawesomeLink = EXT:t3sbootstrap/Resources/Public/Contrib/Fontawesome/css/fontawesomeLink.css
-#[global]
-#[{$bootstrap.config.faLinkIcons} == 1 && {$bootstrap.extconf.fontawesomeCss} == 2]
-#page.includeCSS.t3sfontawesomeProLink = EXT:t3sbootstrap/Resources/Public/Contrib/Fontawesome/css/fontawesomeProLink.css
-#[global]
 [{$bootstrap.config.faLinkIcons} == 1 && {$bootstrap.extconf.fontawesomeCss} == 3]
 page.includeCSS.t3sfontawesomeLink = EXT:t3sbootstrap/Resources/Public/Contrib/Fontawesome/css/fontawesome6Link.css
 [global]
diff --git a/Configuration/TypoScript/Page/IncludeLocal.typoscript b/Configuration/TypoScript/Page/IncludeLocal.typoscript
index 74d0aebb..0430225b 100644
--- a/Configuration/TypoScript/Page/IncludeLocal.typoscript
+++ b/Configuration/TypoScript/Page/IncludeLocal.typoscript
@@ -26,8 +26,13 @@ page {
 		jquery.if.isFalse = {$bootstrap.disable.jquery}
 
 		popper = /fileadmin/T3SB/Resources/Public/JS/popper.js
+		popper.if.isFalse = {$bootstrap.bundle.enable}
 
 		bootstrap = /fileadmin/T3SB/Resources/Public/JS/bootstrap.min.js
+		bootstrap.if.isFalse = {$bootstrap.bundle.enable}
+
+		bootstrapBundle = /fileadmin/T3SB/Resources/Public/JS/bootstrap.bundle.min.js
+		bootstrapBundle.if.isTrue = {$bootstrap.bundle.enable}
 
 	}
 
diff --git a/Configuration/TypoScript/Page/Template.typoscript b/Configuration/TypoScript/Page/Template.typoscript
index 82769e9e..cf951e7e 100644
--- a/Configuration/TypoScript/Page/Template.typoscript
+++ b/Configuration/TypoScript/Page/Template.typoscript
@@ -94,6 +94,8 @@ page {
 			config {
 				jqueryHeader = {$bootstrap.config.jqueryHeader}
 				navbarPlusicon = {$bootstrap.config.navbarPlusicon}
+				colorMode.enable = {$bootstrap.colorMode.enable}
+				colorMode.position = {$bootstrap.colorMode.position}
 			}
 		}
 		dataProcessing {
@@ -347,13 +349,5 @@ lib.t3sbs.dispose.value = Nicht mehr anzeigen
 page.10.settings.indexedsearch.targetPid = {$plugin.tx_indexedsearch.settings.targetPid}
 [global]
 
-plugin.tx_felogin_login {
-	view {
-		templateRootPaths {
-			10 = EXT:t3sbootstrap/Resources/Private/Extensions/felogin/Resources/Private/Templates/
-		}
-	}
-}
-
 // import outsourced setup
 @import 'fileadmin/T3SB/Configuration/TypoScript/t3sbsetup.typoscript'
diff --git a/Configuration/TypoScript/constants.typoscript b/Configuration/TypoScript/constants.typoscript
index 32922605..aebfb1f3 100644
--- a/Configuration/TypoScript/constants.typoscript
+++ b/Configuration/TypoScript/constants.typoscript
@@ -78,7 +78,7 @@ bootstrap.cdn {
 	glightbox = 3.2.0
 	# cat=bootstrap-cdn/b-version/50; type=small; label=Jarallax: set to 2 only to get v2.x (latest)
 	jarallax = 2
-	# cat=bootstrap-cdn/b-version/60; type=small; label=Swiper: set to 9 only to get v9.x (latest)
+	# cat=bootstrap-cdn/b-version/60; type=small; label=Swiper: set to 10 only to get v10.x (latest)
 	swiper = 10
 
 	# cat=bootstrap-cdn/c-integrity/10; type=small; label=Bootstrap CSS:
@@ -216,6 +216,9 @@ bootstrap.pages.override {
 #-------------------------------------------------------------------------------
 #	NOT in Constant Editor
 #-------------------------------------------------------------------------------
+# include Bootstraps bundle.min.js (include popper.js)
+bootstrap.bundle.enable = 1
+
 # Pagebrowser - MORE INFO: https://docs.typo3.org/typo3cms/TyposcriptReference/ContentObjects/Hmenu/Index.html#special-browse
 bootstrap.pagebrowser.enable = 0
 bootstrap.pagebrowser.prevnextToSection = 1
@@ -311,6 +314,13 @@ bootstrap.cookieconsent {
 	location = true
 }
 
+# Color mode toggler - only if "bootstrap.config.navbarDarkMode" is disabled
+bootstrap.colorMode {
+	enable = 0
+	# e.g.: position-fixed bottom-0 start-0 mb-3 ms-3 | info: https://getbootstrap.com/docs/5.3/utilities/position/#center-elements
+	position = position-fixed bottom-0 end-0 mb-3 me-3
+}
+
 styles.content.allowTags:= addToList( s, mark)
 
 # in px (preset)
@@ -346,5 +356,9 @@ bootstrap.config.footerSticky = 0
 bootstrap.config.compress = 
 bootstrap.config.navbarBreakpoint = 
 
+[loaded('felogin')]
+plugin.tx_felogin_login.view.templateRootPath = EXT:t3sbootstrap/Resources/Private/Extensions/felogin/Resources/Private/Templates/
+[global]
+
 // import outsourced constants from DB
 @import 'fileadmin/T3SB/Configuration/TypoScript/t3sbconstants.typoscript'
diff --git a/Configuration/TypoScript/setup.typoscript b/Configuration/TypoScript/setup.typoscript
index 8c576bc2..c996aa72 100644
--- a/Configuration/TypoScript/setup.typoscript
+++ b/Configuration/TypoScript/setup.typoscript
@@ -136,7 +136,8 @@ temp.pagelayout {
 # Google Tag Manager
 #
 ##########################################################################################################
-[{$bootstrap.gtm} && request.getCookieParams()['cookieconsent_status'] == 'allow']
+
+[{$bootstrap.gtm} && {$bootstrap.extconf.cookieconsent} == 1 && request.getCookieParams()['cookieconsent_status'] == 'allow']
 page.includeJS.gtm = https://www.googletagmanager.com/gtag/js?id={$bootstrap.gtm}
 page.includeJS.gtm.external = 1
 page.includeJS.gtm.async = 1
diff --git a/Contrib/scssphp/bin/pscss b/Contrib/scssphp/bin/pscss
index b70d499f..0f009d6b 100644
--- a/Contrib/scssphp/bin/pscss
+++ b/Contrib/scssphp/bin/pscss
@@ -38,8 +38,8 @@ $embedSourceMap = false;
 /**
  * Parse argument
  *
- * @param integer $i
- * @param array $options
+ * @param int      $i
+ * @param string[] $options
  *
  * @return string|null
  */
@@ -166,7 +166,7 @@ EOT;
 
 if (isset($arguments[0]) && file_exists($arguments[0])) {
     $inputFile = $arguments[0];
-    $data = @file_get_contents($inputFile);
+    $data = file_get_contents($inputFile);
 } else {
     $data = '';
 
diff --git a/Contrib/scssphp/composer.json b/Contrib/scssphp/composer.json
index affee419..f81203dd 100644
--- a/Contrib/scssphp/composer.json
+++ b/Contrib/scssphp/composer.json
@@ -42,7 +42,7 @@
         "symfony/phpunit-bridge": "^5.1",
         "thoughtbot/bourbon": "^7.0",
         "twbs/bootstrap": "~5.0",
-        "twbs/bootstrap4": "4.6.0",
+        "twbs/bootstrap4": "4.6.1",
         "zurb/foundation": "~6.5"
     },
     "repositories": [
@@ -50,34 +50,16 @@
             "type": "package",
             "package": {
                 "name": "sass/sass-spec",
-                "version": "2021.09.15",
+                "version": "2022.08.19",
                 "source": {
                     "type": "git",
                     "url": "https://github.com/sass/sass-spec.git",
-                    "reference": "eb2d7a0865c1faf0b55a39ff962b24aca9b4c955"
+                    "reference": "2bdc199723a3445d5badac3ac774105698f08861"
                 },
                 "dist": {
                     "type": "zip",
-                    "url": "https://api.github.com/repos/sass/sass-spec/zipball/eb2d7a0865c1faf0b55a39ff962b24aca9b4c955",
-                    "reference": "eb2d7a0865c1faf0b55a39ff962b24aca9b4c955",
-                    "shasum": ""
-                }
-            }
-        },
-        {
-            "type": "package",
-            "package": {
-                "name": "thoughtbot/bourbon",
-                "version": "v7.0.0",
-                "source": {
-                    "type": "git",
-                    "url": "https://github.com/thoughtbot/bourbon.git",
-                    "reference": "fbe338ee6807e7f7aa996d82c8a16f248bb149b3"
-                },
-                "dist": {
-                    "type": "zip",
-                    "url": "https://api.github.com/repos/thoughtbot/bourbon/zipball/fbe338ee6807e7f7aa996d82c8a16f248bb149b3",
-                    "reference": "fbe338ee6807e7f7aa996d82c8a16f248bb149b3",
+                    "url": "https://api.github.com/repos/sass/sass-spec/zipball/2bdc199723a3445d5badac3ac774105698f08861",
+                    "reference": "2bdc199723a3445d5badac3ac774105698f08861",
                     "shasum": ""
                 }
             }
@@ -104,16 +86,16 @@
             "type": "package",
             "package": {
                 "name": "twbs/bootstrap4",
-                "version": "v4.6.0",
+                "version": "v4.6.1",
                 "source": {
                     "type": "git",
                     "url": "https://github.com/twbs/bootstrap.git",
-                    "reference": "6ffb0b48e455430f8a5359ed689ad64c1143fac2"
+                    "reference": "043a03c95a2ad6738f85b65e53b9dbdfb03b8d10"
                 },
                 "dist": {
                     "type": "zip",
-                    "url": "https://api.github.com/repos/twbs/bootstrap/zipball/6ffb0b48e455430f8a5359ed689ad64c1143fac2",
-                    "reference": "6ffb0b48e455430f8a5359ed689ad64c1143fac2",
+                    "url": "https://api.github.com/repos/twbs/bootstrap/zipball/043a03c95a2ad6738f85b65e53b9dbdfb03b8d10",
+                    "reference": "043a03c95a2ad6738f85b65e53b9dbdfb03b8d10",
                     "shasum": ""
                 }
             }
@@ -121,6 +103,15 @@
     ],
     "bin": ["bin/pscss"],
     "config": {
-        "sort-packages": true
+        "sort-packages": true,
+        "allow-plugins": {
+            "bamarni/composer-bin-plugin": true
+        }
+    },
+    "extra": {
+        "bamarni-bin": {
+            "forward-command": false,
+            "bin-links": false
+        }
     }
 }
diff --git a/Contrib/scssphp/phpcs.xml.dist b/Contrib/scssphp/phpcs.xml.dist
deleted file mode 100644
index b162dbd6..00000000
--- a/Contrib/scssphp/phpcs.xml.dist
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-    
-        
-        
-
-        
-
-    
-
diff --git a/Contrib/scssphp/src/Base/Range.php b/Contrib/scssphp/src/Base/Range.php
index 0c5f5840..31d5ec56 100644
--- a/Contrib/scssphp/src/Base/Range.php
+++ b/Contrib/scssphp/src/Base/Range.php
@@ -34,8 +34,8 @@ class Range
     /**
      * Initialize range
      *
-     * @param integer|float $first
-     * @param integer|float $last
+     * @param int|float $first
+     * @param int|float $last
      */
     public function __construct($first, $last)
     {
@@ -46,9 +46,9 @@ public function __construct($first, $last)
     /**
      * Test for inclusion in range
      *
-     * @param integer|float $value
+     * @param int|float $value
      *
-     * @return boolean
+     * @return bool
      */
     public function includes($value)
     {
diff --git a/Contrib/scssphp/src/Block.php b/Contrib/scssphp/src/Block.php
index 3ae49d00..96668dc6 100644
--- a/Contrib/scssphp/src/Block.php
+++ b/Contrib/scssphp/src/Block.php
@@ -22,12 +22,12 @@
 class Block
 {
     /**
-     * @var string
+     * @var string|null
      */
     public $type;
 
     /**
-     * @var \ScssPhp\ScssPhp\Block
+     * @var Block|null
      */
     public $parent;
 
@@ -37,17 +37,17 @@ class Block
     public $sourceName;
 
     /**
-     * @var integer
+     * @var int
      */
     public $sourceIndex;
 
     /**
-     * @var integer
+     * @var int
      */
     public $sourceLine;
 
     /**
-     * @var integer
+     * @var int
      */
     public $sourceColumn;
 
@@ -67,7 +67,7 @@ class Block
     public $children;
 
     /**
-     * @var \ScssPhp\ScssPhp\Block|null
+     * @var Block|null
      */
     public $selfParent;
 }
diff --git a/Contrib/scssphp/src/Block/AtRootBlock.php b/Contrib/scssphp/src/Block/AtRootBlock.php
new file mode 100644
index 00000000..41842c26
--- /dev/null
+++ b/Contrib/scssphp/src/Block/AtRootBlock.php
@@ -0,0 +1,37 @@
+type = Type::T_AT_ROOT;
+    }
+}
diff --git a/Contrib/scssphp/src/Block/CallableBlock.php b/Contrib/scssphp/src/Block/CallableBlock.php
new file mode 100644
index 00000000..a18a87c2
--- /dev/null
+++ b/Contrib/scssphp/src/Block/CallableBlock.php
@@ -0,0 +1,45 @@
+type = $type;
+    }
+}
diff --git a/Contrib/scssphp/src/Block/ContentBlock.php b/Contrib/scssphp/src/Block/ContentBlock.php
new file mode 100644
index 00000000..87084980
--- /dev/null
+++ b/Contrib/scssphp/src/Block/ContentBlock.php
@@ -0,0 +1,38 @@
+type = Type::T_INCLUDE;
+    }
+}
diff --git a/Contrib/scssphp/src/Block/DirectiveBlock.php b/Contrib/scssphp/src/Block/DirectiveBlock.php
new file mode 100644
index 00000000..b1d3d1a8
--- /dev/null
+++ b/Contrib/scssphp/src/Block/DirectiveBlock.php
@@ -0,0 +1,37 @@
+type = Type::T_DIRECTIVE;
+    }
+}
diff --git a/Contrib/scssphp/src/Block/EachBlock.php b/Contrib/scssphp/src/Block/EachBlock.php
new file mode 100644
index 00000000..b3289579
--- /dev/null
+++ b/Contrib/scssphp/src/Block/EachBlock.php
@@ -0,0 +1,37 @@
+type = Type::T_EACH;
+    }
+}
diff --git a/Contrib/scssphp/src/Block/ElseBlock.php b/Contrib/scssphp/src/Block/ElseBlock.php
new file mode 100644
index 00000000..6abb4d77
--- /dev/null
+++ b/Contrib/scssphp/src/Block/ElseBlock.php
@@ -0,0 +1,27 @@
+type = Type::T_ELSE;
+    }
+}
diff --git a/Contrib/scssphp/src/Block/ElseifBlock.php b/Contrib/scssphp/src/Block/ElseifBlock.php
new file mode 100644
index 00000000..4622bca7
--- /dev/null
+++ b/Contrib/scssphp/src/Block/ElseifBlock.php
@@ -0,0 +1,32 @@
+type = Type::T_ELSEIF;
+    }
+}
diff --git a/Contrib/scssphp/src/Block/ForBlock.php b/Contrib/scssphp/src/Block/ForBlock.php
new file mode 100644
index 00000000..a9cf6733
--- /dev/null
+++ b/Contrib/scssphp/src/Block/ForBlock.php
@@ -0,0 +1,47 @@
+type = Type::T_FOR;
+    }
+}
diff --git a/Contrib/scssphp/src/Block/IfBlock.php b/Contrib/scssphp/src/Block/IfBlock.php
new file mode 100644
index 00000000..9f21bf88
--- /dev/null
+++ b/Contrib/scssphp/src/Block/IfBlock.php
@@ -0,0 +1,37 @@
+
+     */
+    public $cases = [];
+
+    public function __construct()
+    {
+        $this->type = Type::T_IF;
+    }
+}
diff --git a/Contrib/scssphp/src/Block/MediaBlock.php b/Contrib/scssphp/src/Block/MediaBlock.php
new file mode 100644
index 00000000..c49ee1b2
--- /dev/null
+++ b/Contrib/scssphp/src/Block/MediaBlock.php
@@ -0,0 +1,37 @@
+type = Type::T_MEDIA;
+    }
+}
diff --git a/Contrib/scssphp/src/Block/NestedPropertyBlock.php b/Contrib/scssphp/src/Block/NestedPropertyBlock.php
new file mode 100644
index 00000000..1ea4a6c8
--- /dev/null
+++ b/Contrib/scssphp/src/Block/NestedPropertyBlock.php
@@ -0,0 +1,37 @@
+type = Type::T_NESTED_PROPERTY;
+    }
+}
diff --git a/Contrib/scssphp/src/Block/WhileBlock.php b/Contrib/scssphp/src/Block/WhileBlock.php
new file mode 100644
index 00000000..ac18d4e0
--- /dev/null
+++ b/Contrib/scssphp/src/Block/WhileBlock.php
@@ -0,0 +1,32 @@
+type = Type::T_WHILE;
+    }
+}
diff --git a/Contrib/scssphp/src/Cache.php b/Contrib/scssphp/src/Cache.php
index a9ab2e1e..9731c60a 100644
--- a/Contrib/scssphp/src/Cache.php
+++ b/Contrib/scssphp/src/Cache.php
@@ -132,7 +132,7 @@ public function getCache($operation, $what, $options = [], $lastModified = null)
                 (\is_null($lastModified) || $cacheTime > $lastModified) &&
                 $cacheTime + self::$gcLifetime > time()
             ) {
-                $c = @file_get_contents($fileCache);
+                $c = file_get_contents($fileCache);
                 $c = unserialize($c);
 
                 if (\is_array($c) && isset($c['value'])) {
diff --git a/Contrib/scssphp/src/Colors.php b/Contrib/scssphp/src/Colors.php
index e836e3f4..2df39992 100644
--- a/Contrib/scssphp/src/Colors.php
+++ b/Contrib/scssphp/src/Colors.php
@@ -204,10 +204,10 @@ public static function colorNameToRGBa($colorName)
     /**
      * Reverse conversion : from RGBA to a color name if possible
      *
-     * @param integer $r
-     * @param integer $g
-     * @param integer $b
-     * @param integer|float $a
+     * @param int       $r
+     * @param int       $g
+     * @param int       $b
+     * @param int|float $a
      *
      * @return string|null
      */
diff --git a/Contrib/scssphp/src/Compiler.php b/Contrib/scssphp/src/Compiler.php
index 9c3d7e15..ecafc8cb 100644
--- a/Contrib/scssphp/src/Compiler.php
+++ b/Contrib/scssphp/src/Compiler.php
@@ -13,6 +13,17 @@
 namespace ScssPhp\ScssPhp;
 
 use ScssPhp\ScssPhp\Base\Range;
+use ScssPhp\ScssPhp\Block\AtRootBlock;
+use ScssPhp\ScssPhp\Block\CallableBlock;
+use ScssPhp\ScssPhp\Block\DirectiveBlock;
+use ScssPhp\ScssPhp\Block\EachBlock;
+use ScssPhp\ScssPhp\Block\ElseBlock;
+use ScssPhp\ScssPhp\Block\ElseifBlock;
+use ScssPhp\ScssPhp\Block\ForBlock;
+use ScssPhp\ScssPhp\Block\IfBlock;
+use ScssPhp\ScssPhp\Block\MediaBlock;
+use ScssPhp\ScssPhp\Block\NestedPropertyBlock;
+use ScssPhp\ScssPhp\Block\WhileBlock;
 use ScssPhp\ScssPhp\Compiler\CachedResult;
 use ScssPhp\ScssPhp\Compiler\Environment;
 use ScssPhp\ScssPhp\Exception\CompilerException;
@@ -137,6 +148,7 @@ class Compiler
     public static $emptyString  = [Type::T_STRING, '"', []];
     public static $with         = [Type::T_KEYWORD, 'with'];
     public static $without      = [Type::T_KEYWORD, 'without'];
+    private static $emptyArgumentList = [Type::T_LIST, '', [], []];
 
     /**
      * @var array
@@ -154,7 +166,7 @@ class Compiler
 
     /**
      * @var array
-     * @phpstan-var array
+     * @phpstan-var array
      */
     protected $userFunctions = [];
     /**
@@ -199,9 +211,15 @@ class Compiler
     private $charset = true;
 
     /**
-     * @var string|\ScssPhp\ScssPhp\Formatter
+     * @var Formatter
      */
-    protected $formatter = Expanded::class;
+    protected $formatter;
+
+    /**
+     * @var string
+     * @phpstan-var class-string
+     */
+    private $configuredFormatter = Expanded::class;
 
     /**
      * @var Environment
@@ -369,7 +387,7 @@ public function getCompileOptions()
             'encoding'           => $this->encoding,
             'sourceMap'          => serialize($this->sourceMap),
             'sourceMapOptions'   => $this->sourceMapOptions,
-            'formatter'          => $this->formatter,
+            'formatter'          => $this->configuredFormatter,
             'legacyImportPath'   => $this->legacyCwdImportPath,
         ];
 
@@ -490,7 +508,7 @@ public function compileString($source, $path = null)
             $tree         = $this->parser->parse($source);
             $this->parser = null;
 
-            $this->formatter = new $this->formatter();
+            $this->formatter = new $this->configuredFormatter();
             $this->rootBlock = null;
             $this->rootEnv   = $this->pushEnv($tree);
 
@@ -517,6 +535,7 @@ public function compileString($source, $path = null)
                     $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions);
                 }
             }
+            assert($this->scope !== null);
 
             $out = $this->formatter->format($this->scope, $sourceMapGenerator);
 
@@ -530,6 +549,7 @@ public function compileString($source, $path = null)
             $sourceMap = null;
 
             if (! empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) {
+                assert($sourceMapGenerator !== null);
                 $sourceMap = $sourceMapGenerator->generateJson($prefix);
                 $sourceMapUrl = null;
 
@@ -643,7 +663,7 @@ protected function parserFactory($path)
      * @param array $target
      * @param array $origin
      *
-     * @return boolean
+     * @return bool
      */
     protected function isSelfExtend($target, $origin)
     {
@@ -659,7 +679,7 @@ protected function isSelfExtend($target, $origin)
     /**
      * Push extends
      *
-     * @param array      $target
+     * @param string[]   $target
      * @param array      $origin
      * @param array|null $block
      *
@@ -702,9 +722,9 @@ protected function makeOutputBlock($type, $selectors = null)
             $out->sourceLine   = $this->env->block->sourceLine;
             $out->sourceColumn = $this->env->block->sourceColumn;
         } else {
-            $out->sourceName   = null;
-            $out->sourceLine   = null;
-            $out->sourceColumn = null;
+            $out->sourceName = isset($this->sourceNames[$this->sourceIndex]) ? $this->sourceNames[$this->sourceIndex] : '(stdin)';
+            $out->sourceLine = $this->sourceLine;
+            $out->sourceColumn = $this->sourceColumn;
         }
 
         return $out;
@@ -722,6 +742,7 @@ protected function compileRoot(Block $rootBlock)
         $this->rootBlock = $this->scope = $this->makeOutputBlock(Type::T_ROOT);
 
         $this->compileChildrenNoReturn($rootBlock->children, $this->scope);
+        assert($this->scope !== null);
         $this->flattenSelectors($this->scope);
         $this->missingSelectors();
     }
@@ -731,7 +752,7 @@ protected function compileRoot(Block $rootBlock)
      *
      * @return void
      */
-    public function missingSelectors()
+    protected function missingSelectors()
     {
         foreach ($this->extends as $extend) {
             if (isset($extend[3])) {
@@ -749,7 +770,7 @@ public function missingSelectors()
             $origin = $this->collapseSelectors($origin);
 
             $this->sourceLine = $block[Parser::SOURCE_LINE];
-            throw $this->errorMsg("\"$origin\" failed to @extend \"$target\". The selector \"$target\" was not found.");
+            throw $this->error("\"$origin\" failed to @extend \"$target\". The selector \"$target\" was not found.");
         }
     }
 
@@ -803,6 +824,7 @@ protected function flattenSelectors(OutputBlock $block, $parentKey = null)
             }
 
             if ($placeholderSelector && 0 === \count($block->selectors) && null !== $parentKey) {
+                assert($block->parent !== null);
                 unset($block->parent->children[$parentKey]);
 
                 return;
@@ -852,10 +874,10 @@ protected function glueFunctionSelectors($parts)
     /**
      * Match extends
      *
-     * @param array   $selector
-     * @param array   $out
-     * @param integer $from
-     * @param boolean $initial
+     * @param array $selector
+     * @param array $out
+     * @param int   $from
+     * @param bool  $initial
      *
      * @return void
      */
@@ -988,7 +1010,7 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true)
      * @param string $part
      * @param array  $matches
      *
-     * @return boolean
+     * @return bool
      */
     protected function isPseudoSelector($part, &$matches)
     {
@@ -1050,11 +1072,11 @@ protected function pushOrMergeExtentedSelector(&$out, $extended)
     /**
      * Match extends single
      *
-     * @param array   $rawSingle
-     * @param array   $outOrigin
-     * @param boolean $initial
+     * @param array $rawSingle
+     * @param array $outOrigin
+     * @param bool  $initial
      *
-     * @return boolean
+     * @return bool
      */
     protected function matchExtendsSingle($rawSingle, &$outOrigin, $initial = true)
     {
@@ -1269,11 +1291,13 @@ protected function combineSelectorSingle($base, $other)
      */
     protected function compileMedia(Block $media)
     {
+        assert($media instanceof MediaBlock);
         $this->pushEnv($media);
 
         $mediaQueries = $this->compileMediaQuery($this->multiplyMedia($this->env));
 
         if (! empty($mediaQueries)) {
+            assert($this->scope !== null);
             $previousScope = $this->scope;
             $parentScope = $this->mediaParent($this->scope);
 
@@ -1346,7 +1370,7 @@ protected function mediaParent(OutputBlock $scope)
     /**
      * Compile directive
      *
-     * @param \ScssPhp\ScssPhp\Block|array $directive
+     * @param DirectiveBlock|array                   $directive
      * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
      *
      * @return void
@@ -1412,6 +1436,7 @@ protected function compileDirectiveName($directiveName)
      */
     protected function compileAtRoot(Block $block)
     {
+        assert($block instanceof AtRootBlock);
         $env     = $this->pushEnv($block);
         $envs    = $this->compactEnv($env);
         list($with, $without) = $this->compileWith(isset($block->with) ? $block->with : null);
@@ -1438,7 +1463,7 @@ protected function compileAtRoot(Block $block)
 
         if (
             ! $selfParent->selectors &&
-            isset($block->parent) && $block->parent &&
+            isset($block->parent) &&
             isset($block->parent->selectors) && $block->parent->selectors
         ) {
             $selfParent = $block->parent;
@@ -1446,13 +1471,15 @@ protected function compileAtRoot(Block $block)
 
         $this->env = $this->filterWithWithout($envs, $with, $without);
 
+        assert($this->scope !== null);
         $saveScope   = $this->scope;
         $this->scope = $this->filterScopeWithWithout($saveScope, $with, $without);
 
         // propagate selfParent to the children where they still can be useful
         $this->compileChildrenNoReturn($block->children, $this->scope, $selfParent);
 
-        $this->scope = $this->completeScope($this->scope, $saveScope);
+        assert($this->scope !== null);
+        $this->completeScope($this->scope, $saveScope);
         $this->scope = $saveScope;
         $this->env   = $this->extractEnv($envs);
 
@@ -1460,7 +1487,7 @@ protected function compileAtRoot(Block $block)
     }
 
     /**
-     * Filter at-root scope depending of with/without option
+     * Filter at-root scope depending on with/without option
      *
      * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope
      * @param array                                  $with
@@ -1476,6 +1503,7 @@ protected function filterScopeWithWithout($scope, $with, $without)
         if ($scope->type === Type::T_ROOT) {
             return $scope;
         }
+        assert($this->rootBlock !== null);
 
         // start from the root
         while ($scope->parent && $scope->parent->type !== Type::T_ROOT) {
@@ -1543,7 +1571,7 @@ protected function filterScopeWithWithout($scope, $with, $without)
      */
     protected function completeScope($scope, $previousScope)
     {
-        if (! $scope->type && (! $scope->selectors || ! \count($scope->selectors)) && \count($scope->lines)) {
+        if (! $scope->type && ! $scope->selectors && \count($scope->lines)) {
             $scope->selectors = $this->findScopeSelectors($previousScope, $scope->depth);
         }
 
@@ -1560,7 +1588,7 @@ protected function completeScope($scope, $previousScope)
      * Find a selector by the depth node in the scope
      *
      * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope
-     * @param integer                                $depth
+     * @param int                                    $depth
      *
      * @return array
      */
@@ -1584,9 +1612,11 @@ protected function findScopeSelectors($scope, $depth)
     /**
      * Compile @at-root's with: inclusion / without: exclusion into 2 lists uses to filter scope/env later
      *
-     * @param array $withCondition
+     * @param array|null $withCondition
      *
      * @return array
+     *
+     * @phpstan-return array{array, array}
      */
     protected function compileWith($withCondition)
     {
@@ -1606,9 +1636,10 @@ protected function compileWith($withCondition)
                 }
             }
 
-            if ($this->mapHasKey($withCondition, static::$with)) {
+            $withConfig = $this->mapGet($withCondition, static::$with);
+            if ($withConfig !== null) {
                 $without = []; // cancel the default
-                $list = $this->coerceList($this->libMapGet([$withCondition, static::$with]));
+                $list = $this->coerceList($withConfig);
 
                 foreach ($list[2] as $item) {
                     $keyword = $this->compileStringContent($this->coerceString($item));
@@ -1617,9 +1648,10 @@ protected function compileWith($withCondition)
                 }
             }
 
-            if ($this->mapHasKey($withCondition, static::$without)) {
+            $withoutConfig = $this->mapGet($withCondition, static::$without);
+            if ($withoutConfig !== null) {
                 $without = []; // cancel the default
-                $list = $this->coerceList($this->libMapGet([$withCondition, static::$without]));
+                $list = $this->coerceList($withoutConfig);
 
                 foreach ($list[2] as $item) {
                     $keyword = $this->compileStringContent($this->coerceString($item));
@@ -1669,7 +1701,7 @@ protected function filterWithWithout($envs, $with, $without)
      * @param array                                                         $with
      * @param array                                                         $without
      *
-     * @return boolean
+     * @return bool
      */
     protected function isWith($block, $with, $without)
     {
@@ -1679,6 +1711,7 @@ protected function isWith($block, $with, $without)
             }
 
             if ($block->type === Type::T_DIRECTIVE) {
+                assert($block instanceof DirectiveBlock || $block instanceof OutputBlock);
                 if (isset($block->name)) {
                     return $this->testWithWithout($this->compileDirectiveName($block->name), $with, $without);
                 } elseif (isset($block->selectors) && preg_match(',@(\w+),ims', json_encode($block->selectors), $m)) {
@@ -1714,7 +1747,7 @@ protected function isWith($block, $with, $without)
      * @param array  $with
      * @param array  $without
      *
-     * @return boolean
+     * @return bool
      *   true if the block should be kept, false to reject
      */
     protected function testWithWithout($what, $with, $without)
@@ -1749,10 +1782,12 @@ protected function compileKeyframeBlock(Block $block, $selectors)
 
         $this->scope = $this->makeOutputBlock($block->type, $selectors);
         $this->scope->depth = 1;
+        assert($this->scope->parent !== null);
         $this->scope->parent->children[] = $this->scope;
 
         $this->compileChildrenNoReturn($block->children, $this->scope);
 
+        assert($this->scope !== null);
         $this->scope = $this->scope->parent;
         $this->env   = $this->extractEnv($envs);
 
@@ -1769,6 +1804,7 @@ protected function compileKeyframeBlock(Block $block, $selectors)
      */
     protected function compileNestedPropertiesBlock(Block $block, OutputBlock $out)
     {
+        assert($block instanceof NestedPropertyBlock);
         $prefix = $this->compileValue($block->prefix) . '-';
 
         $nested = $this->makeOutputBlock($block->type);
@@ -1787,6 +1823,7 @@ protected function compileNestedPropertiesBlock(Block $block, OutputBlock $out)
                     break;
 
                 case Type::T_NESTED_PROPERTY:
+                    assert($child[1] instanceof NestedPropertyBlock);
                     array_unshift($child[1]->prefix[2], $prefix);
                     break;
             }
@@ -1808,11 +1845,12 @@ protected function compileNestedBlock(Block $block, $selectors)
         $this->pushEnv($block);
 
         $this->scope = $this->makeOutputBlock($block->type, $selectors);
+        assert($this->scope->parent !== null);
         $this->scope->parent->children[] = $this->scope;
 
         // wrap assign children in a block
         // except for @font-face
-        if ($block->type !== Type::T_DIRECTIVE || $this->compileDirectiveName($block->name) !== 'font-face') {
+        if (!$block instanceof DirectiveBlock || $this->compileDirectiveName($block->name) !== 'font-face') {
             // need wrapping?
             $needWrapping = false;
 
@@ -1841,6 +1879,7 @@ protected function compileNestedBlock(Block $block, $selectors)
 
         $this->compileChildrenNoReturn($block->children, $this->scope);
 
+        assert($this->scope !== null);
         $this->scope = $this->scope->parent;
 
         $this->popEnv();
@@ -1869,10 +1908,12 @@ protected function compileNestedBlock(Block $block, $selectors)
     protected function compileBlock(Block $block)
     {
         $env = $this->pushEnv($block);
+        assert($block->selectors !== null);
         $env->selectors = $this->evalSelectors($block->selectors);
 
         $out = $this->makeOutputBlock(null);
 
+        assert($this->scope !== null);
         $this->scope->children[] = $out;
 
         if (\count($block->children)) {
@@ -1890,6 +1931,7 @@ protected function compileBlock(Block $block)
 
             // and revert for the following children of the same block
             if ($selfParentSelectors) {
+                assert($block->selfParent !== null);
                 $block->selfParent->selectors = $selfParentSelectors;
             }
         }
@@ -1901,8 +1943,8 @@ protected function compileBlock(Block $block)
     /**
      * Compile the value of a comment that can have interpolation
      *
-     * @param array   $value
-     * @param boolean $pushEnv
+     * @param array $value
+     * @param bool  $pushEnv
      *
      * @return string
      */
@@ -1945,6 +1987,7 @@ protected function compileComment($block)
         $out = $this->makeOutputBlock(Type::T_COMMENT);
         $out->lines[] = $this->compileCommentValue($block, true);
 
+        assert($this->scope !== null);
         $this->scope->children[] = $out;
     }
 
@@ -1959,7 +2002,11 @@ protected function evalSelectors($selectors)
     {
         $this->shouldEvaluate = false;
 
-        $selectors = array_map([$this, 'evalSelector'], $selectors);
+        $evaluatedSelectors = [];
+        foreach ($selectors as $selector) {
+            $evaluatedSelectors[] = $this->evalSelector($selector);
+        }
+        $selectors = $evaluatedSelectors;
 
         // after evaluating interpolates, we might need a second pass
         if ($this->shouldEvaluate) {
@@ -1970,7 +2017,7 @@ protected function evalSelectors($selectors)
             try {
                 $isValid = $parser->parseSelector($buffer, $newSelectors, true);
             } catch (ParserException $e) {
-                throw $this->errorMsg($e->getMessage());
+                throw $this->error($e->getMessage());
             }
 
             if ($isValid) {
@@ -1987,6 +2034,8 @@ protected function evalSelectors($selectors)
      * @param array $selector
      *
      * @return array
+     *
+     * @phpstan-impure
      */
     protected function evalSelector($selector)
     {
@@ -1999,6 +2048,8 @@ protected function evalSelector($selector)
      * @param array $part
      *
      * @return array
+     *
+     * @phpstan-impure
      */
     protected function evalSelectorPart($part)
     {
@@ -2012,8 +2063,8 @@ protected function evalSelectorPart($part)
                 }
             } elseif (
                 \is_string($p) && \strlen($p) >= 2 &&
-                ($first = $p[0]) && ($first === '"' || $first === "'") &&
-                substr($p, -1) === $first
+                ($p[0] === '"' || $p[0] === "'") &&
+                substr($p, -1) === $p[0]
             ) {
                 $p = substr($p, 1, -1);
             }
@@ -2219,7 +2270,7 @@ protected function compileSelectorPart($piece)
      *
      * @param array $selector
      *
-     * @return boolean
+     * @return bool
      */
     protected function hasSelectorPlaceholder($selector)
     {
@@ -2258,7 +2309,7 @@ protected function pushCallStack($name = '')
             $msg = $this->callStackMessage(true, 100);
             $msg = 'Infinite calling loop';
 
-            throw $this->errorMsg($msg);
+            throw $this->error($msg);
         }
     }
 
@@ -2299,9 +2350,9 @@ protected function compileChildren($stms, OutputBlock $out, $traceName = '')
     }
 
     /**
-     * Compile children and throw exception if unexpected `@return`
+     * Compile children and throw exception if unexpected at-return
      *
-     * @param array                                  $stms
+     * @param array[]                                $stms
      * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
      * @param \ScssPhp\ScssPhp\Block                 $selfParent
      * @param string                                 $traceName
@@ -2316,19 +2367,19 @@ protected function compileChildrenNoReturn($stms, OutputBlock $out, $selfParent
 
         foreach ($stms as $stm) {
             if ($selfParent && isset($stm[1]) && \is_object($stm[1]) && $stm[1] instanceof Block) {
+                $oldSelfParent = $stm[1]->selfParent;
                 $stm[1]->selfParent = $selfParent;
                 $ret = $this->compileChild($stm, $out);
-                $stm[1]->selfParent = null;
+                $stm[1]->selfParent = $oldSelfParent;
             } elseif ($selfParent && \in_array($stm[0], [Type::T_INCLUDE, Type::T_EXTEND])) {
                 $stm['selfParent'] = $selfParent;
                 $ret = $this->compileChild($stm, $out);
-                unset($stm['selfParent']);
             } else {
                 $ret = $this->compileChild($stm, $out);
             }
 
             if (isset($ret)) {
-                throw $this->errorMsg('@return may only be used within a function');
+                throw $this->error('@return may only be used within a function');
             }
         }
 
@@ -2380,7 +2431,7 @@ protected function evaluateMediaQuery($queryList)
                 $queryString = $this->compileMediaQuery([$queryList[$kql]]);
                 $queryString = reset($queryString);
 
-                if (strpos($queryString, '@media ') === 0) {
+                if ($queryString !== false && strpos($queryString, '@media ') === 0) {
                     $queryString = substr($queryString, 7);
                     $queries = [];
 
@@ -2636,9 +2687,9 @@ protected function mergeMediaTypes($type1, $type2)
      *
      * @param array                                  $rawPath
      * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
-     * @param boolean                                $once
+     * @param bool                                   $once
      *
-     * @return boolean
+     * @return bool
      */
     protected function compileImport($rawPath, OutputBlock $out, $once = false)
     {
@@ -2773,7 +2824,7 @@ protected function appendRootDirective($line, $out, $allowed = [Type::T_COMMENT]
         // insert the directive as a comment
         $child = $this->makeOutputBlock(Type::T_COMMENT);
         $child->lines[]      = $line;
-        $child->sourceName   = $this->sourceNames[$this->sourceIndex];
+        $child->sourceName   = $this->sourceNames[$this->sourceIndex] ?: '(stdin)';
         $child->sourceLine   = $this->sourceLine;
         $child->sourceColumn = $this->sourceColumn;
 
@@ -2836,7 +2887,7 @@ protected function compileChild($child, OutputBlock $out)
             $this->sourceIndex  = isset($child[Parser::SOURCE_INDEX]) ? $child[Parser::SOURCE_INDEX] : null;
             $this->sourceLine   = isset($child[Parser::SOURCE_LINE]) ? $child[Parser::SOURCE_LINE] : -1;
             $this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ? $child[Parser::SOURCE_COLUMN] : -1;
-        } elseif (\is_array($child) && isset($child[1]->sourceLine)) {
+        } elseif (\is_array($child) && isset($child[1]->sourceLine) && $child[1] instanceof Block) {
             $this->sourceIndex  = $child[1]->sourceIndex;
             $this->sourceLine   = $child[1]->sourceLine;
             $this->sourceColumn = $child[1]->sourceColumn;
@@ -3044,6 +3095,7 @@ protected function compileChild($child, OutputBlock $out)
             case Type::T_MIXIN:
             case Type::T_FUNCTION:
                 list(, $block) = $child;
+                assert($block instanceof CallableBlock);
                 // the block need to be able to go up to it's parent env to resolve vars
                 $block->parentEnv = $this->getStoreEnv();
                 $this->set(static::$namespaces[$block->type] . $block->name, $block, true);
@@ -3054,14 +3106,14 @@ protected function compileChild($child, OutputBlock $out)
                     $replacedSel = $this->replaceSelfSelector($sel);
 
                     if ($replacedSel !== $sel) {
-                        throw $this->errorMsg('Parent selectors aren\'t allowed here.');
+                        throw $this->error('Parent selectors aren\'t allowed here.');
                     }
 
                     $results = $this->evalSelectors([$sel]);
 
                     foreach ($results as $result) {
                         if (\count($result) !== 1) {
-                            throw $this->errorMsg('complex selectors may not be extended.');
+                            throw $this->error('complex selectors may not be extended.');
                         }
 
                         // only use the first one
@@ -3071,6 +3123,7 @@ protected function compileChild($child, OutputBlock $out)
                         if (! $selectors && isset($child['selfParent'])) {
                             $selectors = $this->multiplySelectors($this->env, $child['selfParent']);
                         }
+                        assert($selectors !== null);
 
                         if (\count($result) > 1) {
                             $replacement = implode(', ', $result);
@@ -3094,6 +3147,7 @@ protected function compileChild($child, OutputBlock $out)
 
             case Type::T_IF:
                 list(, $if) = $child;
+                assert($if instanceof IfBlock);
 
                 if ($this->isTruthy($this->reduce($if->cond, true))) {
                     return $this->compileChildren($if->children, $out);
@@ -3101,8 +3155,8 @@ protected function compileChild($child, OutputBlock $out)
 
                 foreach ($if->cases as $case) {
                     if (
-                        $case->type === Type::T_ELSE ||
-                        $case->type === Type::T_ELSEIF && $this->isTruthy($this->reduce($case->cond))
+                        $case instanceof ElseBlock ||
+                        $case instanceof ElseifBlock && $this->isTruthy($this->reduce($case->cond))
                     ) {
                         return $this->compileChildren($case->children, $out);
                     }
@@ -3111,6 +3165,7 @@ protected function compileChild($child, OutputBlock $out)
 
             case Type::T_EACH:
                 list(, $each) = $child;
+                assert($each instanceof EachBlock);
 
                 $list = $this->coerceList($this->reduce($each->list), ',', true);
 
@@ -3145,6 +3200,7 @@ protected function compileChild($child, OutputBlock $out)
 
             case Type::T_WHILE:
                 list(, $while) = $child;
+                assert($while instanceof WhileBlock);
 
                 while ($this->isTruthy($this->reduce($while->cond, true))) {
                     $ret = $this->compileChildren($while->children, $out);
@@ -3157,6 +3213,7 @@ protected function compileChild($child, OutputBlock $out)
 
             case Type::T_FOR:
                 list(, $for) = $child;
+                assert($for instanceof ForBlock);
 
                 $startNumber = $this->assertNumber($this->reduce($for->start, true));
                 $endNumber = $this->assertNumber($this->reduce($for->end, true));
@@ -3214,9 +3271,11 @@ protected function compileChild($child, OutputBlock $out)
                 $mixin = $this->get(static::$namespaces['mixin'] . $name, false);
 
                 if (! $mixin) {
-                    throw $this->errorMsg("Undefined mixin $name");
+                    throw $this->error("Undefined mixin $name");
                 }
 
+                assert($mixin instanceof CallableBlock);
+
                 $callingScope = $this->getStoreEnv();
 
                 // push scope, apply args
@@ -3227,7 +3286,7 @@ protected function compileChild($child, OutputBlock $out)
                 // and assign this fake parent to childs
                 $selfParent = null;
 
-                if (isset($child['selfParent']) && isset($child['selfParent']->selectors)) {
+                if (isset($child['selfParent']) && $child['selfParent'] instanceof Block && isset($child['selfParent']->selectors)) {
                     $selfParent = $child['selfParent'];
                 } else {
                     $parentSelectors = $this->multiplySelectors($this->env);
@@ -3237,7 +3296,7 @@ protected function compileChild($child, OutputBlock $out)
                         $parent->selectors = $parentSelectors;
 
                         foreach ($mixin->children as $k => $child) {
-                            if (isset($child[1]) && \is_object($child[1]) && $child[1] instanceof Block) {
+                            if (isset($child[1]) && $child[1] instanceof Block) {
                                 $mixin->children[$k][1]->parent = $parent;
                             }
                         }
@@ -3271,7 +3330,7 @@ protected function compileChild($child, OutputBlock $out)
                 if (! empty($mixin->parentEnv)) {
                     $this->env->declarationScopeParent = $mixin->parentEnv;
                 } else {
-                    throw $this->errorMsg("@mixin $name() without parentEnv");
+                    throw $this->error("@mixin $name() without parentEnv");
                 }
 
                 $this->compileChildrenNoReturn($mixin->children, $out, $selfParent, $this->env->marker . ' ' . $name);
@@ -3338,11 +3397,13 @@ protected function compileChild($child, OutputBlock $out)
                 $line  = $this->sourceLine;
                 $value = $this->compileValue($this->reduce($value, true));
 
-                throw $this->errorMsg("File $fname on line $line ERROR: $value\n");
+                throw $this->error("File $fname on line $line ERROR: $value\n");
 
             default:
-                throw $this->errorMsg("unknown child type: $child[0]");
+                throw $this->error("unknown child type: $child[0]");
         }
+
+        return null;
     }
 
     /**
@@ -3389,7 +3450,7 @@ protected function expToString($exp, $keepParens = false)
      *
      * @param array|Number $value
      *
-     * @return boolean
+     * @return bool
      */
     public function isTruthy($value)
     {
@@ -3401,7 +3462,7 @@ public function isTruthy($value)
      *
      * @param string $value
      *
-     * @return boolean
+     * @return bool
      */
     protected function isImmediateRelationshipCombinator($value)
     {
@@ -3413,7 +3474,7 @@ protected function isImmediateRelationshipCombinator($value)
      *
      * @param array $value
      *
-     * @return boolean
+     * @return bool
      */
     protected function shouldEval($value)
     {
@@ -3436,7 +3497,7 @@ protected function shouldEval($value)
      * Reduce value
      *
      * @param array|Number $value
-     * @param boolean $inExp
+     * @param bool         $inExp
      *
      * @return array|Number
      */
@@ -3477,32 +3538,24 @@ protected function reduce($value, $inExp = false)
                 $ucLType  = ucfirst($ltype);
                 $ucRType  = ucfirst($rtype);
 
+                $shouldEval = $inParens || $inExp;
+
                 // this tries:
                 // 1. op[op name][left type][right type]
-                // 2. op[left type][right type] (passing the op as first arg
+                // 2. op[left type][right type] (passing the op as first arg)
                 // 3. op[op name]
-                $fn = "op${ucOpName}${ucLType}${ucRType}";
-
-                if (
-                    \is_callable([$this, $fn]) ||
-                    (($fn = "op${ucLType}${ucRType}") &&
-                        \is_callable([$this, $fn]) &&
-                        $passOp = true) ||
-                    (($fn = "op${ucOpName}") &&
-                        \is_callable([$this, $fn]) &&
-                        $genOp = true)
-                ) {
-                    $shouldEval = $inParens || $inExp;
-
-                    if (isset($passOp)) {
-                        $out = $this->$fn($op, $left, $right, $shouldEval);
-                    } else {
-                        $out = $this->$fn($left, $right, $shouldEval);
-                    }
+                if (\is_callable([$this, $fn = "op{$ucOpName}{$ucLType}{$ucRType}"])) {
+                    $out = $this->$fn($left, $right, $shouldEval);
+                } elseif (\is_callable([$this, $fn = "op{$ucLType}{$ucRType}"])) {
+                    $out = $this->$fn($op, $left, $right, $shouldEval);
+                } elseif (\is_callable([$this, $fn = "op{$ucOpName}"])) {
+                    $out = $this->$fn($left, $right, $shouldEval);
+                } else {
+                    $out = null;
+                }
 
-                    if (isset($out)) {
-                        return $out;
-                    }
+                if (isset($out)) {
+                    return $out;
                 }
 
                 return $this->expToString($value);
@@ -3827,7 +3880,6 @@ protected function getFunctionReference($name, $safeCopy = false)
 
         // try to find a native lib function
         $normalizedName = $this->normalizeName($name);
-        $libName = null;
 
         if (isset($this->userFunctions[$normalizedName])) {
             // see if we can find a user function
@@ -3840,15 +3892,15 @@ protected function getFunctionReference($name, $safeCopy = false)
 
         // Special functions overriding a CSS function are case-insensitive. We normalize them as lowercase
         // to avoid the deprecation warning about the wrong case being used.
-        if ($lowercasedName === 'min' || $lowercasedName === 'max') {
+        if ($lowercasedName === 'min' || $lowercasedName === 'max' || $lowercasedName === 'rgb' || $lowercasedName === 'rgba' || $lowercasedName === 'hsl' || $lowercasedName === 'hsla') {
             $normalizedName = $lowercasedName;
         }
 
         if (($f = $this->getBuiltinFunction($normalizedName)) && \is_callable($f)) {
+            /** @var string $libName */
             $libName   = $f[1];
             $prototype = isset(static::$$libName) ? static::$$libName : null;
 
-
             // All core functions have a prototype defined. Not finding the
             // prototype can mean 2 things:
             // - the function comes from a child class (deprecated just after)
@@ -3943,6 +3995,10 @@ public function normalizeValue($value)
                     unset($value['enclosing']);
                 }
 
+                if ($value[1] === '' && count($value[2]) > 1) {
+                    $value[1] = ' ';
+                }
+
                 return $value;
 
             case Type::T_STRING:
@@ -4058,8 +4114,8 @@ protected function opAdd($left, $right)
      * Boolean and
      *
      * @param array|Number $left
-     * @param array|Number  $right
-     * @param boolean $shouldEval
+     * @param array|Number $right
+     * @param bool         $shouldEval
      *
      * @return array|Number|null
      */
@@ -4087,7 +4143,7 @@ protected function opAnd($left, $right, $shouldEval)
      *
      * @param array|Number $left
      * @param array|Number $right
-     * @param boolean $shouldEval
+     * @param bool         $shouldEval
      *
      * @return array|Number|null
      */
@@ -4151,7 +4207,7 @@ protected function opColorColor($op, $left, $right)
 
                 case '%':
                     if ($rval == 0) {
-                        throw $this->errorMsg("color: Can't take modulo by zero");
+                        throw $this->error("color: Can't take modulo by zero");
                     }
 
                     $out[] = $lval % $rval;
@@ -4159,7 +4215,7 @@ protected function opColorColor($op, $left, $right)
 
                 case '/':
                     if ($rval == 0) {
-                        throw $this->errorMsg("color: Can't divide by zero");
+                        throw $this->error("color: Can't divide by zero");
                     }
 
                     $out[] = (int) ($lval / $rval);
@@ -4172,7 +4228,7 @@ protected function opColorColor($op, $left, $right)
                     return $this->opNeq($left, $right);
 
                 default:
-                    throw $this->errorMsg("color: unknown op $op");
+                    throw $this->error("color: unknown op $op");
             }
         }
 
@@ -4513,8 +4569,7 @@ public function compileValue($value, $quote = true)
                     // force double quote as string quote for the output in certain cases
                     if (
                         $value[1] === "'" &&
-                        (strpos($content, '"') === false or strpos($content, "'") !== false) &&
-                        strpbrk($content, '{}\\\'') !== false
+                        (strpos($content, '"') === false or strpos($content, "'") !== false)
                     ) {
                         $value[1] = '"';
                     } elseif (
@@ -4567,6 +4622,8 @@ public function compileValue($value, $quote = true)
                     }
                 }
 
+                $separator = $delim === '/' ? ' /' : $delim;
+
                 $prefix_value = '';
 
                 if ($delim !== ' ') {
@@ -4605,7 +4662,7 @@ public function compileValue($value, $quote = true)
                     $filtered[] = $compiled;
                 }
 
-                return $pre . substr(implode("$delim", $filtered), \strlen($prefix_value)) . $post;
+                return $pre . substr(implode($separator, $filtered), \strlen($prefix_value)) . $post;
 
             case Type::T_MAP:
                 $keys     = $value[1];
@@ -4706,7 +4763,7 @@ public function compileValue($value, $quote = true)
                 return $this->compileCommentValue($value);
 
             default:
-                throw $this->errorMsg('unknown value type: ' . json_encode($value));
+                throw $this->error('unknown value type: ' . json_encode($value));
         }
     }
 
@@ -4877,10 +4934,10 @@ protected function multiplySelectors(Environment $env, $selfParent = null)
     /**
      * Join selectors; looks for & to replace, or append parent before child
      *
-     * @param array   $parent
-     * @param array   $child
-     * @param boolean $stillHasSelf
-     * @param array   $selfParentSelectors
+     * @param array $parent
+     * @param array $child
+     * @param bool  $stillHasSelf
+     * @param array $selfParentSelectors
 
      * @return array
      */
@@ -4958,6 +5015,8 @@ protected function multiplyMedia(Environment $env = null, $childQueries = null)
             return $this->multiplyMedia($env->parent, $childQueries);
         }
 
+        assert($env->block instanceof MediaBlock);
+
         $parentQueries = isset($env->block->queryList)
             ? $env->block->queryList
             : [[[Type::T_MEDIA_VALUE, $env->block->value]]];
@@ -5092,7 +5151,7 @@ protected function getStoreEnv()
      *
      * @param string                                $name
      * @param mixed                                 $value
-     * @param boolean                               $shadow
+     * @param bool                                  $shadow
      * @param \ScssPhp\ScssPhp\Compiler\Environment $env
      * @param mixed                                 $valueUnreduced
      *
@@ -5198,9 +5257,9 @@ protected function setRaw($name, $value, Environment $env, $valueUnreduced = nul
      * @internal
      *
      * @param string                                $name
-     * @param boolean                               $shouldThrow
+     * @param bool                                  $shouldThrow
      * @param \ScssPhp\ScssPhp\Compiler\Environment $env
-     * @param boolean                               $unreduced
+     * @param bool                                  $unreduced
      *
      * @return mixed|null
      */
@@ -5254,7 +5313,7 @@ public function get($name, $shouldThrow = true, Environment $env = null, $unredu
         }
 
         if ($shouldThrow) {
-            throw $this->errorMsg("Undefined variable \$$name" . ($maxDepth <= 0 ? ' (infinite recursion)' : ''));
+            throw $this->error("Undefined variable \$$name" . ($maxDepth <= 0 ? ' (infinite recursion)' : ''));
         }
 
         // found nothing
@@ -5267,7 +5326,7 @@ public function get($name, $shouldThrow = true, Environment $env = null, $unredu
      * @param string                                $name
      * @param \ScssPhp\ScssPhp\Compiler\Environment $env
      *
-     * @return boolean
+     * @return bool
      */
     protected function has($name, Environment $env = null)
     {
@@ -5457,7 +5516,7 @@ public function setImportPaths($path)
      *
      * @api
      *
-     * @param integer $numberPrecision
+     * @param int $numberPrecision
      *
      * @return void
      *
@@ -5484,11 +5543,11 @@ public function setOutputStyle($style)
     {
         switch ($style) {
             case OutputStyle::EXPANDED:
-                $this->formatter = Expanded::class;
+                $this->configuredFormatter = Expanded::class;
                 break;
 
             case OutputStyle::COMPRESSED:
-                $this->formatter = Compressed::class;
+                $this->configuredFormatter = Compressed::class;
                 break;
 
             default:
@@ -5506,6 +5565,8 @@ public function setOutputStyle($style)
      * @return void
      *
      * @deprecated Use {@see setOutputStyle} instead.
+     *
+     * @phpstan-param class-string $formatterName
      */
     public function setFormatter($formatterName)
     {
@@ -5514,7 +5575,7 @@ public function setFormatter($formatterName)
         }
         @trigger_error('The method "setFormatter" is deprecated. Use "setOutputStyle" instead.', E_USER_DEPRECATED);
 
-        $this->formatter = $formatterName;
+        $this->configuredFormatter = $formatterName;
     }
 
     /**
@@ -5558,7 +5619,7 @@ public function setCharset($charset)
      *
      * @api
      *
-     * @param integer $sourceMap
+     * @param int $sourceMap
      *
      * @return void
      *
@@ -5655,13 +5716,17 @@ protected function importFile($path, OutputBlock $out)
         // see if tree is cached
         $realPath = realpath($path);
 
+        if ($realPath === false) {
+            $realPath = $path;
+        }
+
         if (substr($path, -5) === '.sass') {
             $this->sourceIndex = \count($this->sourceNames);
             $this->sourceNames[] = $path;
             $this->sourceLine = 1;
             $this->sourceColumn = 1;
 
-            throw $this->errorMsg('The Sass indented syntax is not implemented.');
+            throw $this->error('The Sass indented syntax is not implemented.');
         }
 
         if (isset($this->importCache[$realPath])) {
@@ -5669,7 +5734,7 @@ protected function importFile($path, OutputBlock $out)
 
             $tree = $this->importCache[$realPath];
         } else {
-            $code   = @file_get_contents($path);
+            $code   = file_get_contents($path);
             $parser = $this->parserFactory($path);
             $tree   = $parser->parse($code);
 
@@ -5800,7 +5865,7 @@ public function findImport($url, $currentDir = null)
             }
         }
 
-        throw $this->errorMsg("`$url` file not found for @import");
+        throw $this->error("`$url` file not found for @import");
     }
 
     /**
@@ -5849,7 +5914,7 @@ private function checkImportPathConflicts(array $paths)
             $formattedPrettyPaths[] = '  ' . $this->getPrettyPath($path);
         }
 
-        throw $this->errorMsg("It's not clear which file to import. Found:\n" . implode("\n", $formattedPrettyPaths));
+        throw $this->error("It's not clear which file to import. Found:\n" . implode("\n", $formattedPrettyPaths));
     }
 
     /**
@@ -5960,7 +6025,7 @@ public function setEncoding($encoding)
      *
      * @api
      *
-     * @param boolean $ignoreErrors
+     * @param bool $ignoreErrors
      *
      * @return \ScssPhp\ScssPhp\Compiler
      *
@@ -5998,6 +6063,8 @@ public function getSourcePosition()
      *
      * @param string $msg Message with optional sprintf()-style vararg parameters
      *
+     * @return never
+     *
      * @throws \ScssPhp\ScssPhp\Exception\CompilerException
      *
      * @deprecated use "error" and throw the exception in the caller instead.
@@ -6009,7 +6076,7 @@ public function throwError($msg)
             E_USER_DEPRECATED
         );
 
-        throw $this->errorMsg(...func_get_args());
+        throw $this->error(...func_get_args());
     }
 
     /**
@@ -6017,11 +6084,12 @@ public function throwError($msg)
      *
      * @internal
      *
-     * @param string $msg Message with optional sprintf()-style vararg parameters
+     * @param string                     $msg Message with optional sprintf()-style vararg parameters
+     * @param bool|float|int|string|null ...$args
      *
      * @return CompilerException
      */
-    public function errorMsg($msg, ...$args)
+    public function error($msg, ...$args)
     {
         if ($args) {
             $msg = sprintf($msg, ...$args);
@@ -6074,7 +6142,7 @@ public function errorArgsNumber($functionName, $ExpectedArgs, $nbActual)
         $nbExpected = \count($ExpectedArgs);
 
         if ($nbActual > $nbExpected) {
-            return $this->errorMsg(
+            return $this->error(
                 'Error: Only %d arguments allowed in %s(), but %d were passed.',
                 $nbExpected,
                 $functionName,
@@ -6087,7 +6155,7 @@ public function errorArgsNumber($functionName, $ExpectedArgs, $nbActual)
                 array_unshift($missing, array_pop($ExpectedArgs));
             }
 
-            return $this->errorMsg(
+            return $this->error(
                 'Error: %s() argument%s %s missing.',
                 $functionName,
                 count($missing) > 1 ? 's' : '',
@@ -6099,7 +6167,7 @@ public function errorArgsNumber($functionName, $ExpectedArgs, $nbActual)
     /**
      * Beautify call stack for output
      *
-     * @param boolean  $all
+     * @param bool     $all
      * @param int|null $limit
      *
      * @return string
@@ -6135,6 +6203,8 @@ protected function callStackMessage($all = false, $limit = null)
      *
      * @param string $name
      *
+     * @return void
+     *
      * @throws \Exception
      */
     protected function handleImportLoop($name)
@@ -6151,7 +6221,7 @@ protected function handleImportLoop($name)
             }
 
             if (realpath($file) === $name) {
-                throw $this->errorMsg('An @import loop has been found: %s imports %s', $file, basename($file));
+                throw $this->error('An @import loop has been found: %s imports %s', $file, basename($file));
             }
         }
     }
@@ -6159,8 +6229,8 @@ protected function handleImportLoop($name)
     /**
      * Call SCSS @function
      *
-     * @param Object $func
-     * @param array  $argValues
+     * @param CallableBlock|null $func
+     * @param array              $argValues
      *
      * @return array|Number
      */
@@ -6188,7 +6258,7 @@ protected function callScssFunction($func, $argValues)
         if (! empty($func->parentEnv)) {
             $this->env->declarationScopeParent = $func->parentEnv;
         } else {
-            throw $this->errorMsg("@function $name() without parentEnv");
+            throw $this->error("@function $name() without parentEnv");
         }
 
         $ret = $this->compileChildren($func->children, $tmp, $this->env->marker . ' ' . $name);
@@ -6329,6 +6399,9 @@ protected function sortNativeFunctionArgs($functionName, $prototypes, $args)
         if (\in_array($functionName, ['libRgb', 'libRgba', 'libHsl', 'libHsla'])) {
             // notation 100 127 255 / 0 is in fact a simple list of 4 values
             foreach ($args as $k => $arg) {
+                if (!isset($arg[1])) {
+                    continue; // This happens when using a trailing comma
+                }
                 if ($arg[1][0] === Type::T_LIST && \count($arg[1][2]) === 3) {
                     $args[$k][1][2] = $this->extractSlashAlphaInColorFunction($arg[1][2]);
                 }
@@ -6734,9 +6807,8 @@ private function maybeReduce($reduce, $value)
      *
      * @param array[]    $argDef
      * @param array|null $argValues
-     * @param boolean $storeInEnv
-     * @param boolean $reduce
-     *   only used if $storeInEnv = false
+     * @param bool       $storeInEnv
+     * @param bool       $reduce     only used if $storeInEnv = false
      *
      * @return array
      *
@@ -6762,7 +6834,7 @@ protected function applyArguments($argDef, $argValues, $storeInEnv = true, $redu
         $prototype = ['arguments' => [], 'rest_argument' => null];
         $originalRestArgumentName = null;
 
-        foreach ($argDef as $i => $arg) {
+        foreach ($argDef as $arg) {
             list($name, $default, $isVariable) = $arg;
             $normalizedName = str_replace('_', '-', $name);
 
@@ -6931,14 +7003,18 @@ protected function coerceValue($value)
     }
 
     /**
-     * Coerce something to map
+     * Tries to convert an item to a Sass map
      *
-     * @param array|Number $item
+     * @param Number|array $item
      *
-     * @return array|Number
+     * @return array|null
      */
-    protected function coerceMap($item)
+    private function tryMap($item)
     {
+        if ($item instanceof Number) {
+            return null;
+        }
+
         if ($item[0] === Type::T_MAP) {
             return $item;
         }
@@ -6950,6 +7026,24 @@ protected function coerceMap($item)
             return static::$emptyMap;
         }
 
+        return null;
+    }
+
+    /**
+     * Coerce something to map
+     *
+     * @param array|Number $item
+     *
+     * @return array|Number
+     */
+    protected function coerceMap($item)
+    {
+        $map = $this->tryMap($item);
+
+        if ($map !== null) {
+            return $map;
+        }
+
         return $item;
     }
 
@@ -6958,14 +7052,14 @@ protected function coerceMap($item)
      *
      * @param array|Number $item
      * @param string       $delim
-     * @param boolean      $removeTrailingNull
+     * @param bool         $removeTrailingNull
      *
      * @return array
      */
     protected function coerceList($item, $delim = ',', $removeTrailingNull = false)
     {
         if ($item instanceof Number) {
-            return [Type::T_LIST, $delim, [$item]];
+            return [Type::T_LIST, '', [$item]];
         }
 
         if ($item[0] === Type::T_LIST) {
@@ -6986,29 +7080,17 @@ protected function coerceList($item, $delim = ',', $removeTrailingNull = false)
                 $key = $keys[$i];
                 $value = $values[$i];
 
-                switch ($key[0]) {
-                    case Type::T_LIST:
-                    case Type::T_MAP:
-                    case Type::T_STRING:
-                    case Type::T_NULL:
-                        break;
-
-                    default:
-                        $key = [Type::T_KEYWORD, $this->compileStringContent($this->coerceString($key))];
-                        break;
-                }
-
                 $list[] = [
                     Type::T_LIST,
-                    '',
+                    ' ',
                     [$key, $value]
                 ];
             }
 
-            return [Type::T_LIST, ',', $list];
+            return [Type::T_LIST, $list ? ',' : '', $list];
         }
 
-        return [Type::T_LIST, $delim, [$item]];
+        return [Type::T_LIST, '', [$item]];
     }
 
     /**
@@ -7149,10 +7231,10 @@ protected function coerceColor($value, $inRGBFunction = false)
     }
 
     /**
-     * @param integer|Number $value
-     * @param boolean        $isAlpha
+     * @param int|Number $value
+     * @param bool       $isAlpha
      *
-     * @return integer|mixed
+     * @return int|mixed
      */
     protected function compileRGBAValue($value, $isAlpha = false)
     {
@@ -7164,12 +7246,12 @@ protected function compileRGBAValue($value, $isAlpha = false)
     }
 
     /**
-     * @param mixed         $value
-     * @param integer|float $min
-     * @param integer|float $max
-     * @param boolean       $isInt
+     * @param mixed     $value
+     * @param int|float $min
+     * @param int|float $max
+     * @param bool      $isInt
      *
-     * @return integer|mixed
+     * @return int|mixed
      */
     protected function compileColorPartValue($value, $min, $max, $isInt = true)
     {
@@ -7188,7 +7270,7 @@ protected function compileColorPartValue($value, $min, $max, $isInt = true)
                 } elseif ($value->hasUnit('%')) {
                     $num = $max * $value->getDimension() / 100;
                 } else {
-                    throw $this->errorMsg('Expected %s to have no units or "%%".', $value);
+                    throw $this->error('Expected %s to have no units or "%%".', $value);
                 }
 
                 $value = $num;
@@ -7220,6 +7302,8 @@ protected function compileColorPartValue($value, $min, $max, $isInt = true)
     protected function coerceString($value)
     {
         if ($value[0] === Type::T_STRING) {
+            assert(\is_array($value));
+
             return $value;
         }
 
@@ -7263,7 +7347,7 @@ public function assertString($value, $varName = null)
      *
      * @param array|Number $value
      *
-     * @return integer|float
+     * @return int|float
      *
      * @deprecated
      */
@@ -7296,15 +7380,15 @@ protected function coercePercent($value)
      */
     public function assertMap($value, $varName = null)
     {
-        $value = $this->coerceMap($value);
+        $map = $this->tryMap($value);
 
-        if ($value[0] !== Type::T_MAP) {
+        if ($map === null) {
             $value = $this->compileValue($value);
 
             throw SassScriptException::forArgument("$value is not a map.", $varName);
         }
 
-        return $value;
+        return $map;
     }
 
     /**
@@ -7321,8 +7405,9 @@ public function assertMap($value, $varName = null)
     public function assertList($value)
     {
         if ($value[0] !== Type::T_LIST) {
-            throw $this->errorMsg('expecting list, %s received', $value[0]);
+            throw $this->error('expecting list, %s received', $value[0]);
         }
+        assert(\is_array($value));
 
         return $value;
     }
@@ -7401,7 +7486,7 @@ public function assertNumber($value, $varName = null)
      * @param array|Number $value
      * @param string|null  $varName
      *
-     * @return integer
+     * @return int
      *
      * @throws SassScriptException
      */
@@ -7465,9 +7550,9 @@ protected function fixColor($c)
      *
      * @internal
      *
-     * @param integer $red
-     * @param integer $green
-     * @param integer $blue
+     * @param int $red
+     * @param int $green
+     * @param int $blue
      *
      * @return array
      */
@@ -7492,7 +7577,7 @@ public function toHSL($red, $green, $blue)
                 $h = 60 * ($green - $blue) / $d;
             } elseif ($green == $max) {
                 $h = 60 * ($blue - $red) / $d + 120;
-            } elseif ($blue == $max) {
+            } else {
                 $h = 60 * ($red - $green) / $d + 240;
             }
         }
@@ -7571,9 +7656,9 @@ public function toRGB($hue, $saturation, $lightness)
      *
      * @api
      *
-     * @param integer $hue        H from 0 to 360
-     * @param integer $whiteness  W from 0 to 100
-     * @param integer $blackness  B from 0 to 100
+     * @param int|float $hue        H from 0 to 360
+     * @param int|float $whiteness  W from 0 to 100
+     * @param int|float $blackness  B from 0 to 100
      *
      * @return array
      */
@@ -7603,9 +7688,9 @@ private function HWBtoRGB($hue, $whiteness, $blackness)
      *
      * @api
      *
-     * @param integer $red
-     * @param integer $green
-     * @param integer $blue
+     * @param int $red
+     * @param int $green
+     * @param int $blue
      *
      * @return array
      */
@@ -7624,7 +7709,7 @@ private function RGBtoHWB($red, $green, $blue)
                 $h = 60 * ($green - $blue) / $d;
             } elseif ($green == $max) {
                 $h = 60 * ($blue - $red) / $d + 120;
-            } elseif ($blue == $max) {
+            } else {
                 $h = 60 * ($red - $green) / $d + 240;
             }
         }
@@ -7653,7 +7738,7 @@ protected function libCall($args)
         }
 
         if (! in_array($functionReference[0], [Type::T_FUNCTION_REFERENCE, Type::T_FUNCTION])) {
-            throw $this->errorMsg('Function reference expected, got ' . $functionReference[0]);
+            throw $this->error('Function reference expected, got ' . $functionReference[0]);
         }
 
         $callArgs = [
@@ -7733,7 +7818,6 @@ protected function libIndex($args)
 
         $values = [];
 
-
         foreach ($list[2] as $item) {
             $values[] = $this->normalizeValue($item);
         }
@@ -7749,6 +7833,14 @@ protected function libIndex($args)
         ['channels'],
         ['red', 'green', 'blue'],
         ['red', 'green', 'blue', 'alpha'] ];
+
+    /**
+     * @param array $args
+     * @param array $kwargs
+     * @param string $funcName
+     *
+     * @return array
+     */
     protected function libRgb($args, $kwargs, $funcName = 'rgb')
     {
         switch (\count($args)) {
@@ -7831,14 +7923,7 @@ protected function alterColor(array $args, $operation, $fn)
         $scale = $operation === 'scale';
         $change = $operation === 'change';
 
-        /**
-         * @param string $name
-         * @param float|int $max
-         * @param bool $checkPercent
-         * @param bool $assertPercent
-         *
-         * @return float|int|null
-         */
+        /** @phpstan-var callable(string, float|int, bool=, bool=): (float|int|null) $getParam */
         $getParam = function ($name, $max, $checkPercent = false, $assertPercent = false) use (&$kwargs, $scale, $change) {
             if (!isset($kwargs[$name])) {
                 return null;
@@ -7849,7 +7934,7 @@ protected function alterColor(array $args, $operation, $fn)
 
             if (!$scale && $checkPercent) {
                 if (!$number->hasUnit('%')) {
-                    $warning = $this->errorMsg("{$name} Passing a number `$number` without unit % is deprecated.");
+                    $warning = $this->error("{$name} Passing a number `$number` without unit % is deprecated.");
                     $this->logger->warn($warning->getMessage(), true);
                 }
             }
@@ -7862,7 +7947,11 @@ protected function alterColor(array $args, $operation, $fn)
                 $max = 100;
             }
 
-            return $number->valueInRange($change ? 0 : -$max, $max, $name);
+            if ($scale || $assertPercent) {
+                return $number->valueInRange($change ? 0 : -$max, $max, $name);
+            }
+
+            return $number->valueInRangeWithUnit($change ? 0 : -$max, $max, $name, $checkPercent ? '%' : '');
         };
 
         $alpha = $getParam('alpha', 1);
@@ -7897,7 +7986,6 @@ protected function alterColor(array $args, $operation, $fn)
         $hasRgb = $red !== null || $green !== null || $blue !== null;
         $hasSL = $saturation !== null || $lightness !== null;
         $hasWB = $whiteness !== null || $blackness !== null;
-        $found = false;
 
         if ($hasRgb && ($hasSL || $hasWB || $hue !== null)) {
             throw new SassScriptException(sprintf('RGB parameters may not be passed along with %s parameters.', $hasWB ? 'HWB' : 'HSL'));
@@ -8010,7 +8098,7 @@ protected function libIeHexStr($args)
         $color = $this->coerceColor($args[0]);
 
         if (\is_null($color)) {
-            throw $this->errorMsg('Error: argument `$color` of `ie-hex-str($color)` must be a color');
+            throw $this->error('Error: argument `$color` of `ie-hex-str($color)` must be a color');
         }
 
         $color[4] = isset($color[4]) ? round(255 * $color[4]) : 255;
@@ -8024,7 +8112,7 @@ protected function libRed($args)
         $color = $this->coerceColor($args[0]);
 
         if (\is_null($color)) {
-            throw $this->errorMsg('Error: argument `$color` of `red($color)` must be a color');
+            throw $this->error('Error: argument `$color` of `red($color)` must be a color');
         }
 
         return new Number((int) $color[1], '');
@@ -8036,7 +8124,7 @@ protected function libGreen($args)
         $color = $this->coerceColor($args[0]);
 
         if (\is_null($color)) {
-            throw $this->errorMsg('Error: argument `$color` of `green($color)` must be a color');
+            throw $this->error('Error: argument `$color` of `green($color)` must be a color');
         }
 
         return new Number((int) $color[2], '');
@@ -8048,7 +8136,7 @@ protected function libBlue($args)
         $color = $this->coerceColor($args[0]);
 
         if (\is_null($color)) {
-            throw $this->errorMsg('Error: argument `$color` of `blue($color)` must be a color');
+            throw $this->error('Error: argument `$color` of `blue($color)` must be a color');
         }
 
         return new Number((int) $color[3], '');
@@ -8118,6 +8206,14 @@ protected function libMix($args)
         ['hue', 'saturation'],
         ['hue', 'saturation', 'lightness'],
         ['hue', 'saturation', 'lightness', 'alpha'] ];
+
+    /**
+     * @param array $args
+     * @param array $kwargs
+     * @param string $funcName
+     *
+     * @return array|null
+     */
     protected function libHsl($args, $kwargs, $funcName = 'hsl')
     {
         $args_to_check = $args;
@@ -8142,7 +8238,7 @@ protected function libHsl($args, $kwargs, $funcName = 'hsl')
             throw new SassScriptException('Missing argument $lightness.');
         }
 
-        foreach ($kwargs as $k => $arg) {
+        foreach ($kwargs as $arg) {
             if (in_array($arg[0], [Type::T_FUNCTION_CALL, Type::T_FUNCTION]) && in_array($arg[1], ['min', 'max'])) {
                 return null;
             }
@@ -8248,20 +8344,20 @@ protected function libHwb($args, $kwargs, $funcName = 'hwb')
 
         if (\count($args) == 1) {
             if ($args[0][0] !== Type::T_LIST) {
-                throw $this->errorMsg("Missing elements \$whiteness and \$blackness");
+                throw $this->error("Missing elements \$whiteness and \$blackness");
             }
 
             if (\trim($args[0][1])) {
-                throw $this->errorMsg("\$channels must be a space-separated list.");
+                throw $this->error("\$channels must be a space-separated list.");
             }
 
             if (! empty($args[0]['enclosing'])) {
-                throw $this->errorMsg("\$channels must be an unbracketed list.");
+                throw $this->error("\$channels must be an unbracketed list.");
             }
 
             $args = $args[0][2];
             if (\count($args) > 3) {
-                throw $this->errorMsg("hwb() : Only 3 elements are allowed but ". \count($args) . "were passed");
+                throw $this->error("hwb() : Only 3 elements are allowed but ". \count($args) . "were passed");
             }
 
             $args_to_check = $this->extractSlashAlphaInColorFunction($kwargs['channels'][2]);
@@ -8271,13 +8367,13 @@ protected function libHwb($args, $kwargs, $funcName = 'hwb')
         }
 
         if (\count($args_to_check) < 2) {
-            throw $this->errorMsg("Missing elements \$whiteness and \$blackness");
+            throw $this->error("Missing elements \$whiteness and \$blackness");
         }
         if (\count($args_to_check) < 3) {
-            throw $this->errorMsg("Missing element \$blackness");
+            throw $this->error("Missing element \$blackness");
         }
         if (\count($args_to_check) > 4) {
-            throw $this->errorMsg("hwb() : Only 4 elements are allowed but ". \count($args) . "were passed");
+            throw $this->error("hwb() : Only 4 elements are allowed but ". \count($args) . "were passed");
         }
 
         foreach ($kwargs as $k => $arg) {
@@ -8314,7 +8410,7 @@ protected function libHwb($args, $kwargs, $funcName = 'hwb')
 
             if (! \is_numeric($alpha)) {
                 $val = $this->compileValue($args[3]);
-                throw $this->errorMsg("\$alpha: $val is not a number");
+                throw $this->error("\$alpha: $val is not a number");
             }
         }
 
@@ -8362,6 +8458,13 @@ protected function libBlackness($args, $kwargs, $funcName = 'blackness') {
     }
     */
 
+    /**
+     * @param array     $color
+     * @param int       $idx
+     * @param int|float $amount
+     *
+     * @return array
+     */
     protected function adjustHsl($color, $idx, $amount)
     {
         $hsl = $this->toHSL($color[1], $color[2], $color[3]);
@@ -8483,7 +8586,7 @@ protected function libOpacify($args)
         $color = $this->assertColor($args[0], 'color');
         $amount = $this->assertNumber($args[1], 'amount');
 
-        $color[4] = (isset($color[4]) ? $color[4] : 1) + $amount->valueInRange(0, 1, 'amount');
+        $color[4] = (isset($color[4]) ? $color[4] : 1) + $amount->valueInRangeWithUnit(0, 1, 'amount', '');
         $color[4] = min(1, max(0, $color[4]));
 
         return $color;
@@ -8502,7 +8605,7 @@ protected function libTransparentize($args)
         $color = $this->assertColor($args[0], 'color');
         $amount = $this->assertNumber($args[1], 'amount');
 
-        $color[4] = (isset($color[4]) ? $color[4] : 1) - $amount->valueInRange(0, 1, 'amount');
+        $color[4] = (isset($color[4]) ? $color[4] : 1) - $amount->valueInRangeWithUnit(0, 1, 'amount', '');
         $color[4] = min(1, max(0, $color[4]));
 
         return $color;
@@ -8608,7 +8711,7 @@ protected function libMin($args)
             return $min;
         }
 
-        throw $this->errorMsg('At least one argument must be passed.');
+        throw $this->error('At least one argument must be passed.');
     }
 
     protected static $libMax = ['numbers...'];
@@ -8631,7 +8734,7 @@ protected function libMax($args)
             return $max;
         }
 
-        throw $this->errorMsg('At least one argument must be passed.');
+        throw $this->error('At least one argument must be passed.');
     }
 
     protected static $libLength = ['list'];
@@ -8651,7 +8754,7 @@ protected function libListSeparator($args)
 
         $list = $this->coerceList($args[0]);
 
-        if (\count($list[2]) <= 1 && empty($list['enclosing'])) {
+        if ($list[1] === '' && \count($list[2]) <= 1 && empty($list['enclosing'])) {
             return [Type::T_KEYWORD, 'space'];
         }
 
@@ -8659,6 +8762,10 @@ protected function libListSeparator($args)
             return [Type::T_KEYWORD, 'comma'];
         }
 
+        if ($list[1] === '/') {
+            return [Type::T_KEYWORD, 'slash'];
+        }
+
         return [Type::T_KEYWORD, 'space'];
     }
 
@@ -8666,7 +8773,7 @@ protected function libListSeparator($args)
     protected function libNth($args)
     {
         $list = $this->coerceList($args[0], ',', false);
-        $n = $this->assertNumber($args[1])->getDimension();
+        $n = $this->assertInteger($args[1]);
 
         if ($n > 0) {
             $n--;
@@ -8681,7 +8788,7 @@ protected function libNth($args)
     protected function libSetNth($args)
     {
         $list = $this->coerceList($args[0]);
-        $n = $this->assertNumber($args[1])->getDimension();
+        $n = $this->assertInteger($args[1]);
 
         if ($n > 0) {
             $n--;
@@ -8690,7 +8797,7 @@ protected function libSetNth($args)
         }
 
         if (! isset($list[2][$n])) {
-            throw $this->errorMsg('Invalid argument for "n"');
+            throw $this->error('Invalid argument for "n"');
         }
 
         $list[2][$n] = $args[2];
@@ -8698,23 +8805,72 @@ protected function libSetNth($args)
         return $list;
     }
 
-    protected static $libMapGet = ['map', 'key'];
+    protected static $libMapGet = ['map', 'key', 'keys...'];
     protected function libMapGet($args)
     {
         $map = $this->assertMap($args[0], 'map');
-        $key = $args[1];
+        if (!isset($args[2])) {
+            // BC layer for usages of the function from PHP code rather than from the Sass function
+            $args[2] = self::$emptyArgumentList;
+        }
+        $keys = array_merge([$args[1]], $args[2][2]);
+        $value = static::$null;
 
-        if (! \is_null($key)) {
-            $key = $this->compileStringContent($this->coerceString($key));
+        foreach ($keys as $key) {
+            if (!\is_array($map) || $map[0] !== Type::T_MAP) {
+                return static::$null;
+            }
 
-            for ($i = \count($map[1]) - 1; $i >= 0; $i--) {
-                if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) {
-                    return $map[2][$i];
-                }
+            $map = $this->mapGet($map, $key);
+
+            if ($map === null) {
+                return static::$null;
             }
+
+            $value = $map;
         }
 
-        return static::$null;
+        return $value;
+    }
+
+    /**
+     * Gets the value corresponding to that key in the map
+     *
+     * @param array        $map
+     * @param Number|array $key
+     *
+     * @return Number|array|null
+     */
+    private function mapGet(array $map, $key)
+    {
+        $index = $this->mapGetEntryIndex($map, $key);
+
+        if ($index !== null) {
+            return $map[2][$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Gets the index corresponding to that key in the map entries
+     *
+     * @param array        $map
+     * @param Number|array $key
+     *
+     * @return int|null
+     */
+    private function mapGetEntryIndex(array $map, $key)
+    {
+        $key = $this->compileStringContent($this->coerceString($key));
+
+        for ($i = \count($map[1]) - 1; $i >= 0; $i--) {
+            if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) {
+                return $i;
+            }
+        }
+
+        return null;
     }
 
     protected static $libMapKeys = ['map'];
@@ -8764,12 +8920,28 @@ protected function libMapRemove($args)
         return $map;
     }
 
-    protected static $libMapHasKey = ['map', 'key'];
+    protected static $libMapHasKey = ['map', 'key', 'keys...'];
     protected function libMapHasKey($args)
     {
         $map = $this->assertMap($args[0], 'map');
+        if (!isset($args[2])) {
+            // BC layer for usages of the function from PHP code rather than from the Sass function
+            $args[2] = self::$emptyArgumentList;
+        }
+        $keys = array_merge([$args[1]], $args[2][2]);
+        $lastKey = array_pop($keys);
+
+        foreach ($keys as $key) {
+            $value = $this->mapGet($map, $key);
+
+            if ($value === null || $value instanceof Number || $value[0] !== Type::T_MAP) {
+                return self::$false;
+            }
+
+            $map = $value;
+        }
 
-        return $this->toBool($this->mapHasKey($map, $args[1]));
+        return $this->toBool($this->mapHasKey($map, $lastKey));
     }
 
     /**
@@ -8792,24 +8964,127 @@ private function mapHasKey(array $map, $keyValue)
 
     protected static $libMapMerge = [
         ['map1', 'map2'],
-        ['map-1', 'map-2']
+        ['map-1', 'map-2'],
+        ['map1', 'args...']
     ];
     protected function libMapMerge($args)
     {
         $map1 = $this->assertMap($args[0], 'map1');
-        $map2 = $this->assertMap($args[1], 'map2');
+        $map2 = $args[1];
+        $keys = [];
+        if ($map2[0] === Type::T_LIST && isset($map2[3]) && \is_array($map2[3])) {
+            // This is an argument list for the variadic signature
+            if (\count($map2[2]) === 0) {
+                throw new SassScriptException('Expected $args to contain a key.');
+            }
+            if (\count($map2[2]) === 1) {
+                throw new SassScriptException('Expected $args to contain a value.');
+            }
+            $keys = $map2[2];
+            $map2 = array_pop($keys);
+        }
+        $map2 = $this->assertMap($map2, 'map2');
+
+        return $this->modifyMap($map1, $keys, function ($oldValue) use ($map2) {
+            $nestedMap = $this->tryMap($oldValue);
+
+            if ($nestedMap === null) {
+                return $map2;
+            }
+
+            return $this->mergeMaps($nestedMap, $map2);
+        });
+    }
+
+    /**
+     * @param array    $map
+     * @param array    $keys
+     * @param callable $modify
+     * @param bool     $addNesting
+     *
+     * @return Number|array
+     *
+     * @phpstan-param array $keys
+     * @phpstan-param callable(Number|array): (Number|array) $modify
+     */
+    private function modifyMap(array $map, array $keys, callable $modify, $addNesting = true)
+    {
+        if ($keys === []) {
+            return $modify($map);
+        }
+
+        return $this->modifyNestedMap($map, $keys, $modify, $addNesting);
+    }
+
+    /**
+     * @param array    $map
+     * @param array    $keys
+     * @param callable $modify
+     * @param bool     $addNesting
+     *
+     * @return array
+     *
+     * @phpstan-param non-empty-array $keys
+     * @phpstan-param callable(Number|array): (Number|array) $modify
+     */
+    private function modifyNestedMap(array $map, array $keys, callable $modify, $addNesting)
+    {
+        $key = array_shift($keys);
+
+        $nestedValueIndex = $this->mapGetEntryIndex($map, $key);
+
+        if ($keys === []) {
+            if ($nestedValueIndex !== null) {
+                $map[2][$nestedValueIndex] = $modify($map[2][$nestedValueIndex]);
+            } else {
+                $map[1][] = $key;
+                $map[2][] = $modify(self::$null);
+            }
+
+            return $map;
+        }
+
+        $nestedMap = $nestedValueIndex !== null ? $this->tryMap($map[2][$nestedValueIndex]) : null;
+
+        if ($nestedMap === null && !$addNesting) {
+            return $map;
+        }
+
+        if ($nestedMap === null) {
+            $nestedMap = self::$emptyMap;
+        }
+
+        $newNestedMap = $this->modifyNestedMap($nestedMap, $keys, $modify, $addNesting);
 
+        if ($nestedValueIndex !== null) {
+            $map[2][$nestedValueIndex] = $newNestedMap;
+        } else {
+            $map[1][] = $key;
+            $map[2][] = $newNestedMap;
+        }
+
+        return $map;
+    }
+
+    /**
+     * Merges 2 Sass maps together
+     *
+     * @param array $map1
+     * @param array $map2
+     *
+     * @return array
+     */
+    private function mergeMaps(array $map1, array $map2)
+    {
         foreach ($map2[1] as $i2 => $key2) {
-            $key = $this->compileStringContent($this->coerceString($key2));
+            $map1EntryIndex = $this->mapGetEntryIndex($map1, $key2);
 
-            foreach ($map1[1] as $i1 => $key1) {
-                if ($key === $this->compileStringContent($this->coerceString($key1))) {
-                    $map1[2][$i1] = $map2[2][$i2];
-                    continue 2;
-                }
+            if ($map1EntryIndex !== null) {
+                $map1[2][$map1EntryIndex] = $map2[2][$i2];
+                continue;
             }
 
-            $map1[1][] = $map2[1][$i2];
+            $map1[1][] = $key2;
             $map1[2][] = $map2[2][$i2];
         }
 
@@ -8857,9 +9132,13 @@ protected function libIsBracketed($args)
      *
      * @return string
      * @throws CompilerException
+     *
+     * @deprecated
      */
     protected function listSeparatorForJoin($list1, $sep)
     {
+        @trigger_error(sprintf('The "%s" method is deprecated.', __METHOD__), E_USER_DEPRECATED);
+
         if (! isset($sep)) {
             return $list1[1];
         }
@@ -8876,14 +9155,40 @@ protected function listSeparatorForJoin($list1, $sep)
         }
     }
 
-    protected static $libJoin = ['list1', 'list2', 'separator:null', 'bracketed:auto'];
+    protected static $libJoin = ['list1', 'list2', 'separator:auto', 'bracketed:auto'];
     protected function libJoin($args)
     {
         list($list1, $list2, $sep, $bracketed) = $args;
 
         $list1 = $this->coerceList($list1, ' ', true);
         $list2 = $this->coerceList($list2, ' ', true);
-        $sep   = $this->listSeparatorForJoin($list1, $sep);
+
+        switch ($this->compileStringContent($this->assertString($sep, 'separator'))) {
+            case 'comma':
+                $separator = ',';
+                break;
+
+            case 'space':
+                $separator = ' ';
+                break;
+
+            case 'slash':
+                $separator = '/';
+                break;
+
+            case 'auto':
+                if ($list1[1] !== '' || count($list1[2]) > 1 || !empty($list1['enclosing']) && $list1['enclosing'] !== 'parent') {
+                    $separator = $list1[1] ?: ' ';
+                } elseif ($list2[1] !== '' || count($list2[2]) > 1 || !empty($list2['enclosing']) && $list2['enclosing'] !== 'parent') {
+                    $separator = $list2[1] ?: ' ';
+                } else {
+                    $separator = ' ';
+                }
+                break;
+
+            default:
+                throw SassScriptException::forArgument('Must be "space", "comma", "slash", or "auto".', 'separator');
+        }
 
         if ($bracketed === static::$true) {
             $bracketed = true;
@@ -8910,11 +9215,7 @@ protected function libJoin($args)
             }
         }
 
-        $res = [Type::T_LIST, $sep, array_merge($list1[2], $list2[2])];
-
-        if (isset($list1['enclosing'])) {
-            $res['enlcosing'] = $list1['enclosing'];
-        }
+        $res = [Type::T_LIST, $separator, array_merge($list1[2], $list2[2])];
 
         if ($bracketed) {
             $res['enclosing'] = 'bracket';
@@ -8923,14 +9224,35 @@ protected function libJoin($args)
         return $res;
     }
 
-    protected static $libAppend = ['list', 'val', 'separator:null'];
+    protected static $libAppend = ['list', 'val', 'separator:auto'];
     protected function libAppend($args)
     {
         list($list1, $value, $sep) = $args;
 
         $list1 = $this->coerceList($list1, ' ', true);
-        $sep   = $this->listSeparatorForJoin($list1, $sep);
-        $res   = [Type::T_LIST, $sep, array_merge($list1[2], [$value])];
+
+        switch ($this->compileStringContent($this->assertString($sep, 'separator'))) {
+            case 'comma':
+                $separator = ',';
+                break;
+
+            case 'space':
+                $separator = ' ';
+                break;
+
+            case 'slash':
+                $separator = '/';
+                break;
+
+            case 'auto':
+                $separator = $list1[1] === '' && \count($list1[2]) <= 1 && (empty($list1['enclosing']) || $list1['enclosing'] === 'parent') ? ' ' : $list1[1];
+                break;
+
+            default:
+                throw SassScriptException::forArgument('Must be "space", "comma", "slash", or "auto".', 'separator');
+        }
+
+        $res = [Type::T_LIST, $separator, array_merge($list1[2], [$value])];
 
         if (isset($list1['enclosing'])) {
             $res['enclosing'] = $list1['enclosing'];
@@ -8953,7 +9275,7 @@ protected function libZip($args)
         $result = [Type::T_LIST, ',', $lists];
         if (! \is_null($firstList)) {
             foreach ($firstList[2] as $key => $item) {
-                $list = [Type::T_LIST, '', [$item]];
+                $list = [Type::T_LIST, ' ', [$item]];
 
                 foreach ($argLists as $arg) {
                     if (isset($arg[2][$key])) {
@@ -9045,7 +9367,7 @@ protected function libComparable($args)
             ! $number1 instanceof Number ||
             ! $number2 instanceof Number
         ) {
-            throw $this->errorMsg('Invalid argument(s) for "comparable"');
+            throw $this->error('Invalid argument(s) for "comparable"');
         }
 
         return $this->toBool($number1->isComparableTo($number2));
@@ -9083,7 +9405,7 @@ protected function libStrInsert($args)
             $index = $index - 1;
         }
         if ($index < 0) {
-            $index = Util::mbStrlen($stringContent) + 1 + $index;
+            $index = max(Util::mbStrlen($stringContent) + 1 + $index, 0);
         }
 
         $string[2] = [
@@ -9271,7 +9593,25 @@ protected function libCounter($args)
     protected function libRandom($args)
     {
         if (isset($args[0]) && $args[0] !== static::$null) {
-            $n = $this->assertInteger($args[0], 'limit');
+            $limit = $this->assertNumber($args[0], 'limit');
+
+            if ($limit->hasUnits()) {
+                $unitString = $limit->unitStr();
+                $message = <<addLocationToMessage($message));
+            }
+
+            $n = $this->assertInteger($limit, 'limit');
 
             if ($n < 1) {
                 throw new SassScriptException("\$limit: Must be greater than 0, was $n.");
@@ -9333,6 +9673,8 @@ protected function inspectFormatValue($value, $force_enclosing_display = false)
             ) {
                 $value['enclosing'] = 'forced_' . $value['enclosing'];
                 $force_enclosing_display = true;
+            } elseif (! \count($value[2])) {
+                $value['enclosing'] = 'forced_parent';
             }
 
             foreach ($value[2] as $k => $listelement) {
@@ -9456,17 +9798,17 @@ protected function libIsSuperselector($args)
      * @param array $super
      * @param array $sub
      *
-     * @return boolean
+     * @return bool
      */
     protected function isSuperSelector($super, $sub)
     {
         // one and only one selector for each arg
         if (! $super) {
-            throw $this->errorMsg('Invalid super selector for isSuperSelector()');
+            throw $this->error('Invalid super selector for isSuperSelector()');
         }
 
         if (! $sub) {
-            throw $this->errorMsg('Invalid sub selector for isSuperSelector()');
+            throw $this->error('Invalid sub selector for isSuperSelector()');
         }
 
         if (count($sub) > 1) {
@@ -9537,7 +9879,7 @@ function ($value, $key) use (&$compound) {
      * @param array $superParts
      * @param array $subParts
      *
-     * @return boolean
+     * @return bool
      */
     protected function isSuperPart($superParts, $subParts)
     {
@@ -9566,7 +9908,7 @@ protected function libSelectorAppend($args)
         $args = $args[2];
 
         if (\count($args) < 1) {
-            throw $this->errorMsg('selector-append() needs at least 1 argument');
+            throw $this->error('selector-append() needs at least 1 argument');
         }
 
         $selectors = [];
@@ -9591,34 +9933,31 @@ protected function selectorAppend($selectors)
         $lastSelectors = array_pop($selectors);
 
         if (! $lastSelectors) {
-            throw $this->errorMsg('Invalid selector list in selector-append()');
+            throw $this->error('Invalid selector list in selector-append()');
         }
 
         while (\count($selectors)) {
             $previousSelectors = array_pop($selectors);
 
             if (! $previousSelectors) {
-                throw $this->errorMsg('Invalid selector list in selector-append()');
+                throw $this->error('Invalid selector list in selector-append()');
             }
 
             // do the trick, happening $lastSelector to $previousSelector
             $appended = [];
 
-            foreach ($lastSelectors as $lastSelector) {
-                $previous = $previousSelectors;
-
-                foreach ($lastSelector as $lastSelectorParts) {
-                    foreach ($lastSelectorParts as $lastSelectorPart) {
-                        foreach ($previous as $i => $previousSelector) {
-                            foreach ($previousSelector as $j => $previousSelectorParts) {
-                                $previous[$i][$j][] = $lastSelectorPart;
+            foreach ($previousSelectors as $previousSelector) {
+                foreach ($lastSelectors as $lastSelector) {
+                    $previous = $previousSelector;
+                    foreach ($previousSelector as $j => $previousSelectorParts) {
+                        foreach ($lastSelector as $lastSelectorParts) {
+                            foreach ($lastSelectorParts as $lastSelectorPart) {
+                                $previous[$j][] = $lastSelectorPart;
                             }
                         }
                     }
-                }
 
-                foreach ($previous as $ps) {
-                    $appended[] = $ps;
+                    $appended[] = $previous;
                 }
             }
 
@@ -9641,7 +9980,7 @@ protected function libSelectorExtend($args)
         $extender  = $this->getSelectorArg($extender, 'extender');
 
         if (! $selectors || ! $extendee || ! $extender) {
-            throw $this->errorMsg('selector-extend() invalid arguments');
+            throw $this->error('selector-extend() invalid arguments');
         }
 
         $extended = $this->extendOrReplaceSelectors($selectors, $extendee, $extender);
@@ -9662,7 +10001,7 @@ protected function libSelectorReplace($args)
         $replacement = $this->getSelectorArg($replacement, 'replacement');
 
         if (! $selectors || ! $original || ! $replacement) {
-            throw $this->errorMsg('selector-replace() invalid arguments');
+            throw $this->error('selector-replace() invalid arguments');
         }
 
         $replaced = $this->extendOrReplaceSelectors($selectors, $original, $replacement, true);
@@ -9674,10 +10013,10 @@ protected function libSelectorReplace($args)
      * Extend/replace in selectors
      * used by selector-extend and selector-replace that use the same logic
      *
-     * @param array   $selectors
-     * @param array   $extendee
-     * @param array   $extender
-     * @param boolean $replace
+     * @param array $selectors
+     * @param array $extendee
+     * @param array $extender
+     * @param bool  $replace
      *
      * @return array
      */
@@ -9691,7 +10030,7 @@ protected function extendOrReplaceSelectors($selectors, $extendee, $extender, $r
 
         foreach ($extendee as $es) {
             if (\count($es) !== 1) {
-                throw $this->errorMsg('Can\'t extend complex selector.');
+                throw $this->error('Can\'t extend complex selector.');
             }
 
             // only use the first one
@@ -9729,7 +10068,7 @@ protected function libSelectorNest($args)
         $args = $args[2];
 
         if (\count($args) < 1) {
-            throw $this->errorMsg('selector-nest() needs at least 1 argument');
+            throw $this->error('selector-nest() needs at least 1 argument');
         }
 
         $selectorsMap = [];
@@ -9737,6 +10076,8 @@ protected function libSelectorNest($args)
             $selectorsMap[] = $this->getSelectorArg($arg, 'selector', true);
         }
 
+        assert(!empty($selectorsMap));
+
         $envs = [];
 
         foreach ($selectorsMap as $selectors) {
@@ -9774,7 +10115,7 @@ protected function libSelectorUnify($args)
         $selectors2 = $this->getSelectorArg($selectors2, 'selectors2');
 
         if (! $selectors1 || ! $selectors2) {
-            throw $this->errorMsg('selector-unify() invalid arguments');
+            throw $this->error('selector-unify() invalid arguments');
         }
 
         // only consider the first compound of each
diff --git a/Contrib/scssphp/src/Compiler/Environment.php b/Contrib/scssphp/src/Compiler/Environment.php
index 306b15a6..b205a077 100644
--- a/Contrib/scssphp/src/Compiler/Environment.php
+++ b/Contrib/scssphp/src/Compiler/Environment.php
@@ -31,6 +31,26 @@ class Environment
      */
     public $parent;
 
+    /**
+     * @var Environment|null
+     */
+    public $declarationScopeParent;
+
+    /**
+     * @var Environment|null
+     */
+    public $parentStore;
+
+    /**
+     * @var array|null
+     */
+    public $selectors;
+
+    /**
+     * @var string|null
+     */
+    public $marker;
+
     /**
      * @var array
      */
@@ -42,7 +62,7 @@ class Environment
     public $storeUnreduced;
 
     /**
-     * @var integer
+     * @var int
      */
     public $depth;
 }
diff --git a/Contrib/scssphp/src/Exception/ParserException.php b/Contrib/scssphp/src/Exception/ParserException.php
index 00d77ec9..f0726698 100644
--- a/Contrib/scssphp/src/Exception/ParserException.php
+++ b/Contrib/scssphp/src/Exception/ParserException.php
@@ -22,7 +22,8 @@
 class ParserException extends \Exception implements SassException
 {
     /**
-     * @var array
+     * @var array|null
+     * @phpstan-var array{string, int, int}|null
      */
     private $sourcePosition;
 
@@ -30,6 +31,9 @@ class ParserException extends \Exception implements SassException
      * Get source position
      *
      * @api
+     *
+     * @return array|null
+     * @phpstan-return array{string, int, int}|null
      */
     public function getSourcePosition()
     {
@@ -42,6 +46,10 @@ public function getSourcePosition()
      * @api
      *
      * @param array $sourcePosition
+     *
+     * @return void
+     *
+     * @phpstan-param array{string, int, int} $sourcePosition
      */
     public function setSourcePosition($sourcePosition)
     {
diff --git a/Contrib/scssphp/src/Formatter.php b/Contrib/scssphp/src/Formatter.php
index cc42ae80..6137dc65 100644
--- a/Contrib/scssphp/src/Formatter.php
+++ b/Contrib/scssphp/src/Formatter.php
@@ -25,7 +25,7 @@
 abstract class Formatter
 {
     /**
-     * @var integer
+     * @var int
      */
     public $indentLevel;
 
@@ -60,7 +60,7 @@ abstract class Formatter
     public $assignSeparator;
 
     /**
-     * @var boolean
+     * @var bool
      */
     public $keepSemicolons;
 
@@ -70,12 +70,12 @@ abstract class Formatter
     protected $currentBlock;
 
     /**
-     * @var integer
+     * @var int
      */
     protected $currentLine;
 
     /**
-     * @var integer
+     * @var int
      */
     protected $currentColumn;
 
@@ -239,7 +239,7 @@ protected function block(OutputBlock $block)
      *
      * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
      *
-     * @return boolean
+     * @return bool
      */
     protected function testEmptyChildren($block)
     {
@@ -286,9 +286,18 @@ public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerato
 
         ob_start();
 
-        $this->block($block);
+        try {
+            $this->block($block);
+        } catch (\Exception $e) {
+            ob_end_clean();
+            throw $e;
+        } catch (\Throwable $e) {
+            ob_end_clean();
+            throw $e;
+        }
 
         $out = ob_get_clean();
+        assert($out !== false);
 
         return $out;
     }
@@ -331,6 +340,8 @@ protected function write($str)
                 // If the written line starts is empty, adding a mapping would add it for
                 // a non-existent column as we are at the end of the line
                 if ($line !== '') {
+                    assert($this->currentBlock->sourceLine !== null);
+                    assert($this->currentBlock->sourceName !== null);
                     $this->sourceMapGenerator->addMapping(
                         $this->currentLine,
                         $this->currentColumn,
@@ -346,6 +357,8 @@ protected function write($str)
             }
 
             if ($lastLine !== '') {
+                assert($this->currentBlock->sourceLine !== null);
+                assert($this->currentBlock->sourceName !== null);
                 $this->sourceMapGenerator->addMapping(
                     $this->currentLine,
                     $this->currentColumn,
diff --git a/Contrib/scssphp/src/Formatter/Compressed.php b/Contrib/scssphp/src/Formatter/Compressed.php
index de13c188..58ebe3f1 100644
--- a/Contrib/scssphp/src/Formatter/Compressed.php
+++ b/Contrib/scssphp/src/Formatter/Compressed.php
@@ -50,8 +50,6 @@ public function blockLines(OutputBlock $block)
         foreach ($block->lines as $index => $line) {
             if (substr($line, 0, 2) === '/*' && substr($line, 2, 1) !== '!') {
                 unset($block->lines[$index]);
-            } elseif (substr($line, 0, 3) === '/*!') {
-                $block->lines[$index] = '/*' . substr($line, 3);
             }
         }
 
diff --git a/Contrib/scssphp/src/Formatter/Expanded.php b/Contrib/scssphp/src/Formatter/Expanded.php
index a280416d..6eb4a0cb 100644
--- a/Contrib/scssphp/src/Formatter/Expanded.php
+++ b/Contrib/scssphp/src/Formatter/Expanded.php
@@ -57,7 +57,9 @@ protected function blockLines(OutputBlock $block)
 
         foreach ($block->lines as $index => $line) {
             if (substr($line, 0, 2) === '/*') {
-                $block->lines[$index] = preg_replace('/\r\n?|\n|\f/', $this->break, $line);
+                $replacedLine = preg_replace('/\r\n?|\n|\f/', $this->break, $line);
+                assert($replacedLine !== null);
+                $block->lines[$index] = $replacedLine;
             }
         }
 
diff --git a/Contrib/scssphp/src/Formatter/Nested.php b/Contrib/scssphp/src/Formatter/Nested.php
index 9e729568..d5ed85cc 100644
--- a/Contrib/scssphp/src/Formatter/Nested.php
+++ b/Contrib/scssphp/src/Formatter/Nested.php
@@ -27,7 +27,7 @@
 class Nested extends Formatter
 {
     /**
-     * @var integer
+     * @var int
      */
     private $depth;
 
@@ -68,7 +68,9 @@ protected function blockLines(OutputBlock $block)
 
         foreach ($block->lines as $index => $line) {
             if (substr($line, 0, 2) === '/*') {
-                $block->lines[$index] = preg_replace('/\r\n?|\n|\f/', $this->break, $line);
+                $replacedLine = preg_replace('/\r\n?|\n|\f/', $this->break, $line);
+                assert($replacedLine !== null);
+                $block->lines[$index] = $replacedLine;
             }
         }
 
@@ -221,7 +223,7 @@ protected function block(OutputBlock $block)
      *
      * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
      *
-     * @return boolean
+     * @return bool
      */
     private function hasFlatChild($block)
     {
diff --git a/Contrib/scssphp/src/Formatter/OutputBlock.php b/Contrib/scssphp/src/Formatter/OutputBlock.php
index 88deb2d3..2799656a 100644
--- a/Contrib/scssphp/src/Formatter/OutputBlock.php
+++ b/Contrib/scssphp/src/Formatter/OutputBlock.php
@@ -22,12 +22,12 @@
 class OutputBlock
 {
     /**
-     * @var string
+     * @var string|null
      */
     public $type;
 
     /**
-     * @var integer
+     * @var int
      */
     public $depth;
 
@@ -57,12 +57,12 @@ class OutputBlock
     public $sourceName;
 
     /**
-     * @var integer|null
+     * @var int|null
      */
     public $sourceLine;
 
     /**
-     * @var integer|null
+     * @var int|null
      */
     public $sourceColumn;
 }
diff --git a/Contrib/scssphp/src/Logger/QuietLogger.php b/Contrib/scssphp/src/Logger/QuietLogger.php
index 0f358c64..ad7c0753 100644
--- a/Contrib/scssphp/src/Logger/QuietLogger.php
+++ b/Contrib/scssphp/src/Logger/QuietLogger.php
@@ -14,6 +14,8 @@
 
 /**
  * A logger that silently ignores all messages.
+ *
+ * @final
  */
 class QuietLogger implements LoggerInterface
 {
diff --git a/Contrib/scssphp/src/Logger/StreamLogger.php b/Contrib/scssphp/src/Logger/StreamLogger.php
index f5da6c9c..7db7cc18 100644
--- a/Contrib/scssphp/src/Logger/StreamLogger.php
+++ b/Contrib/scssphp/src/Logger/StreamLogger.php
@@ -14,6 +14,8 @@
 
 /**
  * A logger that prints to a PHP stream (for instance stderr)
+ *
+ * @final
  */
 class StreamLogger implements LoggerInterface
 {
diff --git a/Contrib/scssphp/src/Node.php b/Contrib/scssphp/src/Node.php
index 53019375..fcaf8a95 100644
--- a/Contrib/scssphp/src/Node.php
+++ b/Contrib/scssphp/src/Node.php
@@ -27,7 +27,7 @@ abstract class Node
     public $type;
 
     /**
-     * @var integer
+     * @var int
      */
     public $sourceIndex;
 
diff --git a/Contrib/scssphp/src/Node/Number.php b/Contrib/scssphp/src/Node/Number.php
index 95eb9084..48e711f0 100644
--- a/Contrib/scssphp/src/Node/Number.php
+++ b/Contrib/scssphp/src/Node/Number.php
@@ -38,7 +38,7 @@ class Number extends Node implements \ArrayAccess
     const PRECISION = 10;
 
     /**
-     * @var integer
+     * @var int
      * @deprecated use {Number::PRECISION} instead to read the precision. Configuring it is not supported anymore.
      */
     public static $precision = self::PRECISION;
@@ -81,7 +81,7 @@ class Number extends Node implements \ArrayAccess
     ];
 
     /**
-     * @var integer|float
+     * @var int|float
      */
     private $dimension;
 
@@ -100,7 +100,7 @@ class Number extends Node implements \ArrayAccess
     /**
      * Initialize number
      *
-     * @param integer|float   $dimension
+     * @param int|float       $dimension
      * @param string[]|string $numeratorUnits
      * @param string[]        $denominatorUnits
      *
@@ -147,7 +147,7 @@ public function getDenominatorUnits()
     }
 
     /**
-     * {@inheritdoc}
+     * @return bool
      */
     #[\ReturnTypeWillChange]
     public function offsetExists($offset)
@@ -173,7 +173,7 @@ public function offsetExists($offset)
     }
 
     /**
-     * {@inheritdoc}
+     * @return mixed
      */
     #[\ReturnTypeWillChange]
     public function offsetGet($offset)
@@ -200,7 +200,7 @@ public function offsetGet($offset)
     }
 
     /**
-     * {@inheritdoc}
+     * @return void
      */
     #[\ReturnTypeWillChange]
     public function offsetSet($offset, $value)
@@ -209,8 +209,9 @@ public function offsetSet($offset, $value)
     }
 
     /**
-     * {@inheritdoc}
+     * @return void
      */
+    #[\ReturnTypeWillChange]
     public function offsetUnset($offset)
     {
         throw new \BadMethodCallException('Number is immutable');
@@ -219,13 +220,23 @@ public function offsetUnset($offset)
     /**
      * Returns true if the number is unitless
      *
-     * @return boolean
+     * @return bool
      */
     public function unitless()
     {
         return \count($this->numeratorUnits) === 0 && \count($this->denominatorUnits) === 0;
     }
 
+    /**
+     * Returns true if the number has any units
+     *
+     * @return bool
+     */
+    public function hasUnits()
+    {
+        return !$this->unitless();
+    }
+
     /**
      * Checks whether the number has exactly this unit
      *
@@ -265,7 +276,27 @@ public function valueInRange($min, $max, $name = null)
         try {
             return Util::checkRange('', new Range($min, $max), $this);
         } catch (RangeException $e) {
-            throw SassScriptException::forArgument(sprintf('Expected %s to be within %s%s and %s%3$s', $this, $min, $this->unitStr(), $max), $name);
+            throw SassScriptException::forArgument(sprintf('Expected %s to be within %s%s and %s%3$s.', $this, $min, $this->unitStr(), $max), $name);
+        }
+    }
+
+    /**
+     * @param float|int $min
+     * @param float|int $max
+     * @param string    $name
+     * @param string    $unit
+     *
+     * @return float|int
+     * @throws SassScriptException
+     *
+     * @internal
+     */
+    public function valueInRangeWithUnit($min, $max, $name, $unit)
+    {
+        try {
+            return Util::checkRange('', new Range($min, $max), $this);
+        } catch (RangeException $e) {
+            throw SassScriptException::forArgument(sprintf('Expected %s to be within %s%s and %s%3$s.', $this, $min, $unit, $max), $name);
         }
     }
 
diff --git a/Contrib/scssphp/src/Parser.php b/Contrib/scssphp/src/Parser.php
index 3a3ae628..1c76e7c6 100644
--- a/Contrib/scssphp/src/Parser.php
+++ b/Contrib/scssphp/src/Parser.php
@@ -12,6 +12,18 @@
 
 namespace ScssPhp\ScssPhp;
 
+use ScssPhp\ScssPhp\Block\AtRootBlock;
+use ScssPhp\ScssPhp\Block\CallableBlock;
+use ScssPhp\ScssPhp\Block\ContentBlock;
+use ScssPhp\ScssPhp\Block\DirectiveBlock;
+use ScssPhp\ScssPhp\Block\EachBlock;
+use ScssPhp\ScssPhp\Block\ElseBlock;
+use ScssPhp\ScssPhp\Block\ElseifBlock;
+use ScssPhp\ScssPhp\Block\ForBlock;
+use ScssPhp\ScssPhp\Block\IfBlock;
+use ScssPhp\ScssPhp\Block\MediaBlock;
+use ScssPhp\ScssPhp\Block\NestedPropertyBlock;
+use ScssPhp\ScssPhp\Block\WhileBlock;
 use ScssPhp\ScssPhp\Exception\ParserException;
 use ScssPhp\ScssPhp\Logger\LoggerInterface;
 use ScssPhp\ScssPhp\Logger\QuietLogger;
@@ -74,10 +86,6 @@ class Parser
      * @var array
      */
     private $sourcePositions;
-    /**
-     * @var array|null
-     */
-    private $charset;
     /**
      * The current offset in the buffer
      *
@@ -126,7 +134,7 @@ class Parser
      * @api
      *
      * @param string|null          $sourceName
-     * @param integer              $sourceIndex
+     * @param int                  $sourceIndex
      * @param string|null          $encoding
      * @param Cache|null           $cache
      * @param bool                 $cssOnly
@@ -136,11 +144,9 @@ public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8',
     {
         $this->sourceName       = $sourceName ?: '(stdin)';
         $this->sourceIndex      = $sourceIndex;
-        $this->charset          = null;
         $this->utf8             = ! $encoding || strtolower($encoding) === 'utf-8';
         $this->patternModifiers = $this->utf8 ? 'Aisu' : 'Ais';
         $this->commentsSeen     = [];
-        $this->commentsSeen     = [];
         $this->allowVars        = true;
         $this->cssOnly          = $cssOnly;
         $this->logger = $logger ?: new QuietLogger();
@@ -244,7 +250,6 @@ public function parse($buffer)
         if ($this->cache) {
             $cacheKey = $this->sourceName . ':' . md5($buffer);
             $parseOptions = [
-                'charset' => $this->charset,
                 'utf8' => $this->utf8,
             ];
             $v = $this->cache->getCache('parse', $cacheKey, $parseOptions);
@@ -285,11 +290,8 @@ public function parse($buffer)
             throw $this->parseError('unclosed block');
         }
 
-        if ($this->charset) {
-            array_unshift($this->env->children, $this->charset);
-        }
-
         $this->restoreEncoding();
+        assert($this->env !== null);
 
         if ($this->cache) {
             $this->cache->setCache('parse', $cacheKey, $this->env, $parseOptions);
@@ -306,7 +308,7 @@ public function parse($buffer)
      * @param string       $buffer
      * @param string|array $out
      *
-     * @return boolean
+     * @return bool
      */
     public function parseValue($buffer, &$out)
     {
@@ -335,7 +337,7 @@ public function parseValue($buffer, &$out)
      * @param string|array $out
      * @param bool         $shouldValidate
      *
-     * @return boolean
+     * @return bool
      */
     public function parseSelector($buffer, &$out, $shouldValidate = true)
     {
@@ -369,10 +371,10 @@ public function parseSelector($buffer, &$out, $shouldValidate = true)
      *
      * @api
      *
-     * @param string       $buffer
-     * @param string|array $out
+     * @param string $buffer
+     * @param array  $out
      *
-     * @return boolean
+     * @return bool
      */
     public function parseMediaQueryList($buffer, &$out)
     {
@@ -429,7 +431,7 @@ public function parseMediaQueryList($buffer, &$out)
      * position into $s. Then if a chain fails, use $this->seek($s) to
      * go back where we started.
      *
-     * @return boolean
+     * @return bool
      */
     protected function parseChunk()
     {
@@ -448,7 +450,8 @@ protected function parseChunk()
             ) {
                 ! $this->cssOnly || $this->assertPlainCssValid(false, $s);
 
-                $atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s);
+                $atRoot = new AtRootBlock();
+                $this->registerPushedBlock($atRoot, $s);
                 $atRoot->selector = $selector;
                 $atRoot->with     = $with;
 
@@ -462,7 +465,8 @@ protected function parseChunk()
                 $this->mediaQueryList($mediaQueryList) &&
                 $this->matchChar('{', false)
             ) {
-                $media = $this->pushSpecialBlock(Type::T_MEDIA, $s);
+                $media = new MediaBlock();
+                $this->registerPushedBlock($media, $s);
                 $media->queryList = $mediaQueryList[2];
 
                 return true;
@@ -478,7 +482,8 @@ protected function parseChunk()
             ) {
                 ! $this->cssOnly || $this->assertPlainCssValid(false, $s);
 
-                $mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s);
+                $mixin = new CallableBlock(Type::T_MIXIN);
+                $this->registerPushedBlock($mixin, $s);
                 $mixin->name = $mixinName;
                 $mixin->args = $args;
 
@@ -510,7 +515,8 @@ protected function parseChunk()
                 ];
 
                 if (! empty($hasBlock)) {
-                    $include = $this->pushSpecialBlock(Type::T_INCLUDE, $s);
+                    $include = new ContentBlock();
+                    $this->registerPushedBlock($include, $s);
                     $include->child = $child;
                 } else {
                     $this->append($child, $s);
@@ -600,7 +606,8 @@ protected function parseChunk()
             ) {
                 ! $this->cssOnly || $this->assertPlainCssValid(false, $s);
 
-                $func = $this->pushSpecialBlock(Type::T_FUNCTION, $s);
+                $func = new CallableBlock(Type::T_FUNCTION);
+                $this->registerPushedBlock($func, $s);
                 $func->name = $fnName;
                 $func->args = $args;
 
@@ -632,7 +639,8 @@ protected function parseChunk()
             ) {
                 ! $this->cssOnly || $this->assertPlainCssValid(false, $s);
 
-                $each = $this->pushSpecialBlock(Type::T_EACH, $s);
+                $each = new EachBlock();
+                $this->registerPushedBlock($each, $s);
 
                 foreach ($varNames[2] as $varName) {
                     $each->vars[] = $varName[1];
@@ -661,7 +669,8 @@ protected function parseChunk()
                     $cond = reset($cond[2]);
                 }
 
-                $while = $this->pushSpecialBlock(Type::T_WHILE, $s);
+                $while = new WhileBlock();
+                $this->registerPushedBlock($while, $s);
                 $while->cond = $cond;
 
                 return true;
@@ -681,7 +690,8 @@ protected function parseChunk()
             ) {
                 ! $this->cssOnly || $this->assertPlainCssValid(false, $s);
 
-                $for = $this->pushSpecialBlock(Type::T_FOR, $s);
+                $for = new ForBlock();
+                $this->registerPushedBlock($for, $s);
                 $for->var   = $varName[1];
                 $for->start = $start;
                 $for->end   = $end;
@@ -698,7 +708,8 @@ protected function parseChunk()
             ) {
                 ! $this->cssOnly || $this->assertPlainCssValid(false, $s);
 
-                $if = $this->pushSpecialBlock(Type::T_IF, $s);
+                $if = new IfBlock();
+                $this->registerPushedBlock($if, $s);
 
                 while (
                     $cond[0] === Type::T_LIST &&
@@ -777,20 +788,21 @@ protected function parseChunk()
 
             if (isset($last) && $last[0] === Type::T_IF) {
                 list(, $if) = $last;
+                assert($if instanceof IfBlock);
 
                 if ($this->literal('@else', 5)) {
                     if ($this->matchChar('{', false)) {
-                        $else = $this->pushSpecialBlock(Type::T_ELSE, $s);
+                        $else = new ElseBlock();
                     } elseif (
                         $this->literal('if', 2) &&
                         $this->functionCallArgumentsList($cond, false, '{', false)
                     ) {
-                        $else = $this->pushSpecialBlock(Type::T_ELSEIF, $s);
+                        $else = new ElseifBlock();
                         $else->cond = $cond;
                     }
 
                     if (isset($else)) {
-                        $else->dontAppend = true;
+                        $this->registerPushedBlock($else, $s);
                         $if->cases[] = $else;
 
                         return true;
@@ -806,18 +818,6 @@ protected function parseChunk()
                 $this->valueList($charset) &&
                 $this->end()
             ) {
-                if (! isset($this->charset)) {
-                    $statement = [Type::T_CHARSET, $charset];
-
-                    list($line, $column) = $this->getSourcePosition($s);
-
-                    $statement[static::SOURCE_LINE]   = $line;
-                    $statement[static::SOURCE_COLUMN] = $column;
-                    $statement[static::SOURCE_INDEX]  = $this->sourceIndex;
-
-                    $this->charset = $statement;
-                }
-
                 return true;
             }
 
@@ -828,7 +828,8 @@ protected function parseChunk()
                 ($t1 = $this->supportsQuery($supportQuery)) &&
                 ($t2 = $this->matchChar('{', false))
             ) {
-                $directive = $this->pushSpecialBlock(Type::T_DIRECTIVE, $s);
+                $directive = new DirectiveBlock();
+                $this->registerPushedBlock($directive, $s);
                 $directive->name  = 'supports';
                 $directive->value = $supportQuery;
 
@@ -849,11 +850,12 @@ protected function parseChunk()
                     $dirName = [Type::T_STRING, '', $dirName];
                 }
                 if ($dirName === 'media') {
-                    $directive = $this->pushSpecialBlock(Type::T_MEDIA, $s);
+                    $directive = new MediaBlock();
                 } else {
-                    $directive = $this->pushSpecialBlock(Type::T_DIRECTIVE, $s);
+                    $directive = new DirectiveBlock();
                     $directive->name = $dirName;
                 }
+                $this->registerPushedBlock($directive, $s);
 
                 if (isset($dirValue)) {
                     ! $this->cssOnly || ($dirValue = $this->assertPlainCssValid($dirValue));
@@ -1029,7 +1031,8 @@ protected function parseChunk()
             if ($this->matchChar('{', false)) {
                 ! $this->cssOnly || $this->assertPlainCssValid(false);
 
-                $propBlock = $this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s);
+                $propBlock = new NestedPropertyBlock();
+                $this->registerPushedBlock($propBlock, $s);
                 $propBlock->prefix = $name;
                 $propBlock->hasValue = $foundSomething;
 
@@ -1050,17 +1053,20 @@ protected function parseChunk()
             $block = $this->popBlock();
 
             if (! isset($block->type) || $block->type !== Type::T_IF) {
+                assert($this->env !== null);
+
                 if ($this->env->parent) {
                     $this->append(null); // collect comments before next statement if needed
                 }
             }
 
-            if (isset($block->type) && $block->type === Type::T_INCLUDE) {
+            if ($block instanceof ContentBlock) {
                 $include = $block->child;
+                assert(\is_array($include));
                 unset($block->child);
                 $include[3] = $block;
                 $this->append($include, $s);
-            } elseif (empty($block->dontAppend)) {
+            } elseif (!$block instanceof ElseBlock && !$block instanceof ElseifBlock) {
                 $type = isset($block->type) ? $block->type : Type::T_BLOCK;
                 $this->append([$type, $block], $s);
             }
@@ -1068,6 +1074,7 @@ protected function parseChunk()
             // collect comments just after the block closing if needed
             if ($this->eatWhiteDefault) {
                 $this->whitespace();
+                assert($this->env !== null);
 
                 if ($this->env->comments) {
                     $this->append(null);
@@ -1089,20 +1096,34 @@ protected function parseChunk()
      * Push block onto parse tree
      *
      * @param array|null $selectors
-     * @param integer $pos
+     * @param int        $pos
      *
      * @return Block
      */
     protected function pushBlock($selectors, $pos = 0)
+    {
+        $b = new Block();
+        $b->selectors = $selectors;
+
+        $this->registerPushedBlock($b, $pos);
+
+        return $b;
+    }
+
+    /**
+     * @param Block $b
+     * @param int   $pos
+     *
+     * @return void
+     */
+    private function registerPushedBlock(Block $b, $pos)
     {
         list($line, $column) = $this->getSourcePosition($pos);
 
-        $b = new Block();
         $b->sourceName   = $this->sourceName;
         $b->sourceLine   = $line;
         $b->sourceColumn = $column;
         $b->sourceIndex  = $this->sourceIndex;
-        $b->selectors    = $selectors;
         $b->comments     = [];
         $b->parent       = $this->env;
 
@@ -1122,20 +1143,21 @@ protected function pushBlock($selectors, $pos = 0)
         // collect comments at the beginning of a block if needed
         if ($this->eatWhiteDefault) {
             $this->whitespace();
+            assert($this->env !== null);
 
             if ($this->env->comments) {
                 $this->append(null);
             }
         }
-
-        return $b;
     }
 
     /**
      * Push special (named) block onto parse tree
      *
+     * @deprecated
+     *
      * @param string  $type
-     * @param integer $pos
+     * @param int     $pos
      *
      * @return Block
      */
@@ -1156,6 +1178,7 @@ protected function pushSpecialBlock($type, $pos)
      */
     protected function popBlock()
     {
+        assert($this->env !== null);
 
         // collect comments ending just before of a block closing
         if ($this->env->comments) {
@@ -1184,11 +1207,11 @@ protected function popBlock()
     /**
      * Peek input stream
      *
-     * @param string  $regex
-     * @param array   $out
-     * @param integer $from
+     * @param string $regex
+     * @param array  $out
+     * @param int    $from
      *
-     * @return integer
+     * @return int
      */
     protected function peek($regex, &$out, $from = null)
     {
@@ -1205,7 +1228,9 @@ protected function peek($regex, &$out, $from = null)
     /**
      * Seek to position in input stream (or return current position in input stream)
      *
-     * @param integer $where
+     * @param int $where
+     *
+     * @return void
      */
     protected function seek($where)
     {
@@ -1216,7 +1241,10 @@ protected function seek($where)
      * Assert a parsed part is plain CSS Valid
      *
      * @param array|false $parsed
-     * @param int $startPos
+     * @param int         $startPos
+     *
+     * @return array
+     *
      * @throws ParserException
      */
     protected function assertPlainCssValid($parsed, $startPos = null)
@@ -1244,8 +1272,11 @@ protected function assertPlainCssValid($parsed, $startPos = null)
 
     /**
      * Check a parsed element is plain CSS Valid
+     *
      * @param array $parsed
-     * @return bool|array
+     * @param bool  $allowExpression
+     *
+     * @return array|false
      */
     protected function isPlainCssValidElement($parsed, $allowExpression = false)
     {
@@ -1424,7 +1455,9 @@ protected function isPlainCssValidElement($parsed, $allowExpression = false)
      * @param array  $m     Matches (passed by reference)
      * @param string $delim Delimiter
      *
-     * @return boolean True if match; false otherwise
+     * @return bool True if match; false otherwise
+     *
+     * @phpstan-impure
      */
     protected function matchString(&$m, $delim)
     {
@@ -1460,11 +1493,13 @@ protected function matchString(&$m, $delim)
     /**
      * Try to match something on head of buffer
      *
-     * @param string  $regex
-     * @param array   $out
-     * @param boolean $eatWhitespace
+     * @param string $regex
+     * @param array  $out
+     * @param bool   $eatWhitespace
+     *
+     * @return bool
      *
-     * @return boolean
+     * @phpstan-impure
      */
     protected function match($regex, &$out, $eatWhitespace = null)
     {
@@ -1490,10 +1525,12 @@ protected function match($regex, &$out, $eatWhitespace = null)
     /**
      * Match a single string
      *
-     * @param string  $char
-     * @param boolean $eatWhitespace
+     * @param string $char
+     * @param bool   $eatWhitespace
+     *
+     * @return bool
      *
-     * @return boolean
+     * @phpstan-impure
      */
     protected function matchChar($char, $eatWhitespace = null)
     {
@@ -1517,11 +1554,13 @@ protected function matchChar($char, $eatWhitespace = null)
     /**
      * Match literal string
      *
-     * @param string  $what
-     * @param integer $len
-     * @param boolean $eatWhitespace
+     * @param string $what
+     * @param int    $len
+     * @param bool   $eatWhitespace
      *
-     * @return boolean
+     * @return bool
+     *
+     * @phpstan-impure
      */
     protected function literal($what, $len, $eatWhitespace = null)
     {
@@ -1545,7 +1584,9 @@ protected function literal($what, $len, $eatWhitespace = null)
     /**
      * Match some whitespace
      *
-     * @return boolean
+     * @return bool
+     *
+     * @phpstan-impure
      */
     protected function whitespace()
     {
@@ -1597,19 +1638,19 @@ protected function whitespace()
 
                 if (! $comment) {
                     // single part static comment
-                    $this->appendComment([Type::T_COMMENT, $c]);
+                    $commentStatement = [Type::T_COMMENT, $c];
                 } else {
                     $comment[] = $c;
                     $staticComment = substr($this->buffer, $startCommentCount, $endCommentCount - $startCommentCount);
                     $commentStatement = [Type::T_COMMENT, $staticComment, [Type::T_STRING, '', $comment]];
+                }
 
-                    list($line, $column) = $this->getSourcePosition($startCommentCount);
-                    $commentStatement[self::SOURCE_LINE] = $line;
-                    $commentStatement[self::SOURCE_COLUMN] = $column;
-                    $commentStatement[self::SOURCE_INDEX] = $this->sourceIndex;
+                list($line, $column) = $this->getSourcePosition($startCommentCount);
+                $commentStatement[self::SOURCE_LINE] = $line;
+                $commentStatement[self::SOURCE_COLUMN] = $column;
+                $commentStatement[self::SOURCE_INDEX] = $this->sourceIndex;
 
-                    $this->appendComment($commentStatement);
-                }
+                $this->appendComment($commentStatement);
 
                 $this->commentsSeen[$startCommentCount] = true;
                 $this->count = $endCommentCount;
@@ -1632,9 +1673,13 @@ protected function whitespace()
      * Append comment to current block
      *
      * @param array $comment
+     *
+     * @return void
      */
     protected function appendComment($comment)
     {
+        assert($this->env !== null);
+
         if (! $this->discardComments) {
             $this->env->comments[] = $comment;
         }
@@ -1644,10 +1689,14 @@ protected function appendComment($comment)
      * Append statement to current block
      *
      * @param array|null $statement
-     * @param integer $pos
+     * @param int        $pos
+     *
+     * @return void
      */
     protected function append($statement, $pos = null)
     {
+        assert($this->env !== null);
+
         if (! \is_null($statement)) {
             ! $this->cssOnly || ($statement = $this->assertPlainCssValid($statement, $pos));
 
@@ -1677,11 +1726,15 @@ protected function append($statement, $pos = null)
      */
     protected function last()
     {
+        assert($this->env !== null);
+
         $i = \count($this->env->children) - 1;
 
         if (isset($this->env->children[$i])) {
             return $this->env->children[$i];
         }
+
+        return null;
     }
 
     /**
@@ -1689,7 +1742,7 @@ protected function last()
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function mediaQueryList(&$out)
     {
@@ -1701,7 +1754,7 @@ protected function mediaQueryList(&$out)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function mediaQuery(&$out)
     {
@@ -1755,7 +1808,7 @@ protected function mediaQuery(&$out)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function supportsQuery(&$out)
     {
@@ -1888,7 +1941,7 @@ protected function supportsQuery(&$out)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function mediaExpression(&$out)
     {
@@ -1921,7 +1974,7 @@ protected function mediaExpression(&$out)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function argValues(&$out)
     {
@@ -1946,7 +1999,7 @@ protected function argValues(&$out)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function argValue(&$out)
     {
@@ -2026,10 +2079,12 @@ protected function isKnownGenericDirective($directiveName)
     /**
      * Parse directive value list that considers $vars as keyword
      *
-     * @param array          $out
-     * @param boolean|string $endChar
+     * @param array        $out
+     * @param string|false $endChar
      *
-     * @return boolean
+     * @return bool
+     *
+     * @phpstan-impure
      */
     protected function directiveValue(&$out, $endChar = false)
     {
@@ -2090,7 +2145,7 @@ protected function directiveValue(&$out, $endChar = false)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function valueList(&$out)
     {
@@ -2106,10 +2161,11 @@ protected function valueList(&$out)
      * Parse a function call, where externals () are part of the call
      * and not of the value list
      *
-     * @param $out
-     * @param bool $mandatoryEnclos
+     * @param array       $out
+     * @param bool        $mandatoryEnclos
      * @param null|string $charAfter
-     * @param null|bool $eatWhiteSp
+     * @param null|bool   $eatWhiteSp
+     *
      * @return bool
      */
     protected function functionCallArgumentsList(&$out, $mandatoryEnclos = true, $charAfter = null, $eatWhiteSp = null)
@@ -2146,7 +2202,7 @@ protected function functionCallArgumentsList(&$out, $mandatoryEnclos = true, $ch
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function spaceList(&$out)
     {
@@ -2156,17 +2212,18 @@ protected function spaceList(&$out)
     /**
      * Parse generic list
      *
-     * @param array   $out
-     * @param string  $parseItem The name of the method used to parse items
-     * @param string  $delim
-     * @param boolean $flatten
+     * @param array  $out
+     * @param string $parseItem The name of the method used to parse items
+     * @param string $delim
+     * @param bool   $flatten
      *
-     * @return boolean
+     * @return bool
      */
     protected function genericList(&$out, $parseItem, $delim = '', $flatten = true)
     {
         $s     = $this->count;
         $items = [];
+        /** @var array|Number|null $value */
         $value = null;
 
         while ($this->$parseItem($value)) {
@@ -2180,9 +2237,12 @@ protected function genericList(&$out, $parseItem, $delim = '', $flatten = true)
 
                 $trailing_delim = true;
             } else {
+                assert(\is_array($value) || $value instanceof Number);
                 // if no delim watch that a keyword didn't eat the single/double quote
                 // from the following starting string
                 if ($value[0] === Type::T_KEYWORD) {
+                    assert(\is_array($value));
+                    /** @var string $word */
                     $word = $value[1];
 
                     $last_char = substr($word, -1);
@@ -2207,8 +2267,10 @@ protected function genericList(&$out, $parseItem, $delim = '', $flatten = true)
                             $this->count--;
                         }
 
+                        /** @var array|Number|null $nextValue */
                         $nextValue = null;
                         if ($this->$parseItem($nextValue)) {
+                            assert(\is_array($nextValue) || $nextValue instanceof Number);
                             if ($nextValue[0] === Type::T_KEYWORD && $nextValue[1] === $last_char) {
                                 // bad try, forget it
                                 $this->seek($currentCount);
@@ -2257,11 +2319,13 @@ protected function genericList(&$out, $parseItem, $delim = '', $flatten = true)
     /**
      * Parse expression
      *
-     * @param array   $out
-     * @param boolean $listOnly
-     * @param boolean $lookForExp
+     * @param array $out
+     * @param bool  $listOnly
+     * @param bool  $lookForExp
      *
-     * @return boolean
+     * @return bool
+     *
+     * @phpstan-impure
      */
     protected function expression(&$out, $listOnly = false, $lookForExp = true)
     {
@@ -2322,12 +2386,14 @@ protected function expression(&$out, $listOnly = false, $lookForExp = true)
     /**
      * Parse expression specifically checking for lists in parenthesis or brackets
      *
-     * @param array   $out
-     * @param integer $s
-     * @param string  $closingParen
-     * @param array   $allowedTypes
+     * @param array    $out
+     * @param int      $s
+     * @param string   $closingParen
+     * @param string[] $allowedTypes
+     *
+     * @return bool
      *
-     * @return boolean
+     * @phpstan-param array $allowedTypes
      */
     protected function enclosedExpression(&$out, $s, $closingParen = ')', $allowedTypes = [Type::T_LIST, Type::T_MAP])
     {
@@ -2382,8 +2448,8 @@ protected function enclosedExpression(&$out, $s, $closingParen = ')', $allowedTy
     /**
      * Parse left-hand side of subexpression
      *
-     * @param array   $lhs
-     * @param integer $minP
+     * @param array $lhs
+     * @param int   $minP
      *
      * @return array
      */
@@ -2395,7 +2461,7 @@ protected function expHelper($lhs, $minP)
         $whiteBefore = isset($this->buffer[$this->count - 1]) &&
             ctype_space($this->buffer[$this->count - 1]);
 
-        while ($this->match($operators, $m, false) && static::$precedence[$m[1]] >= $minP) {
+        while ($this->match($operators, $m, false) && static::$precedence[strtolower($m[1])] >= $minP) {
             $whiteAfter = isset($this->buffer[$this->count]) &&
                 ctype_space($this->buffer[$this->count]);
             $varAfter = isset($this->buffer[$this->count]) &&
@@ -2419,7 +2485,7 @@ protected function expHelper($lhs, $minP)
             }
 
             // consume higher-precedence operators on the right-hand side
-            $rhs = $this->expHelper($rhs, static::$precedence[$op] + 1);
+            $rhs = $this->expHelper($rhs, static::$precedence[strtolower($op)] + 1);
 
             $lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter];
 
@@ -2438,7 +2504,7 @@ protected function expHelper($lhs, $minP)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function value(&$out)
     {
@@ -2646,7 +2712,7 @@ protected function value(&$out)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function parenValue(&$out)
     {
@@ -2685,7 +2751,7 @@ protected function parenValue(&$out)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function progid(&$out)
     {
@@ -2718,7 +2784,7 @@ protected function progid(&$out)
      * @param string $name
      * @param array  $func
      *
-     * @return boolean
+     * @return bool
      */
     protected function func($name, &$func)
     {
@@ -2738,6 +2804,10 @@ protected function func($name, &$func)
                     $this->argValues($args) &&
                     $this->matchChar(')')
                 ) {
+                    if (strtolower($name) === 'var' && \count($args) === 2 && $args[1][0] === Type::T_NULL) {
+                        $args[1] = [null, [Type::T_STRING, '', [' ']], false];
+                    }
+
                     $func = [Type::T_FUNCTION_CALL, $name, $args];
 
                     return true;
@@ -2772,7 +2842,7 @@ protected function func($name, &$func)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function argumentList(&$out)
     {
@@ -2817,7 +2887,7 @@ protected function argumentList(&$out)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function argumentDef(&$out)
     {
@@ -2879,7 +2949,7 @@ protected function argumentDef(&$out)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function map(&$out)
     {
@@ -2921,7 +2991,7 @@ protected function map(&$out)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function color(&$out)
     {
@@ -2947,7 +3017,7 @@ protected function color(&$out)
      *
      * @param array $unit
      *
-     * @return boolean
+     * @return bool
      */
     protected function unit(&$unit)
     {
@@ -2957,7 +3027,7 @@ protected function unit(&$unit)
             if (\strlen($this->buffer) === $this->count || ! ctype_digit($this->buffer[$this->count])) {
                 $this->whitespace();
 
-                $unit = new Number($m[1], empty($m[3]) ? '' : $m[3]);
+                $unit = new Node\Number($m[1], empty($m[3]) ? '' : $m[3]);
 
                 return true;
             }
@@ -2972,8 +3042,9 @@ protected function unit(&$unit)
      * Parse string
      *
      * @param array $out
+     * @param bool  $keepDelimWithInterpolation
      *
-     * @return boolean
+     * @return bool
      */
     protected function string(&$out, $keepDelimWithInterpolation = false)
     {
@@ -3054,7 +3125,8 @@ protected function string(&$out, $keepDelimWithInterpolation = false)
 
     /**
      * @param string $out
-     * @param bool $inKeywords
+     * @param bool   $inKeywords
+     *
      * @return bool
      */
     protected function matchEscapeCharacter(&$out, $inKeywords = false)
@@ -3104,10 +3176,10 @@ protected function matchEscapeCharacter(&$out, $inKeywords = false)
     /**
      * Parse keyword or interpolation
      *
-     * @param array   $out
-     * @param boolean $restricted
+     * @param array $out
+     * @param bool  $restricted
      *
-     * @return boolean
+     * @return bool
      */
     protected function mixedKeyword(&$out, $restricted = false)
     {
@@ -3148,14 +3220,14 @@ protected function mixedKeyword(&$out, $restricted = false)
     /**
      * Parse an unbounded string stopped by $end
      *
-     * @param string  $end
-     * @param array   $out
-     * @param string  $nestOpen
-     * @param string  $nestClose
-     * @param boolean $rtrim
+     * @param string $end
+     * @param array  $out
+     * @param string $nestOpen
+     * @param string $nestClose
+     * @param bool   $rtrim
      * @param string $disallow
      *
-     * @return boolean
+     * @return bool
      */
     protected function openString($end, &$out, $nestOpen = null, $nestClose = null, $rtrim = true, $disallow = null)
     {
@@ -3231,9 +3303,9 @@ protected function openString($end, &$out, $nestOpen = null, $nestClose = null,
      * Parser interpolation
      *
      * @param string|array $out
-     * @param boolean      $lookWhite save information about whitespace before and after
+     * @param bool         $lookWhite save information about whitespace before and after
      *
-     * @return boolean
+     * @return bool
      */
     protected function interpolation(&$out, $lookWhite = true)
     {
@@ -3288,7 +3360,7 @@ protected function interpolation(&$out, $lookWhite = true)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function propertyName(&$out)
     {
@@ -3343,7 +3415,7 @@ protected function propertyName(&$out)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function customProperty(&$out)
     {
@@ -3401,9 +3473,9 @@ protected function customProperty(&$out)
      * Parse comma separated selector list
      *
      * @param array $out
-     * @param string|boolean $subSelector
+     * @param string|bool $subSelector
      *
-     * @return boolean
+     * @return bool
      */
     protected function selectors(&$out, $subSelector = false)
     {
@@ -3437,9 +3509,9 @@ protected function selectors(&$out, $subSelector = false)
      * Parse whitespace separated selector list
      *
      * @param array          $out
-     * @param string|boolean $subSelector
+     * @param string|bool $subSelector
      *
-     * @return boolean
+     * @return bool
      */
     protected function selector(&$out, $subSelector = false)
     {
@@ -3495,7 +3567,8 @@ protected function selector(&$out, $subSelector = false)
      * - but this require a better formal selector representation instead of the array we have now
      *
      * @param string $out
-     * @param bool $keepEscapedNumber
+     * @param bool   $keepEscapedNumber
+     *
      * @return bool
      */
     protected function matchEscapeCharacterInSelector(&$out, $keepEscapedNumber = false)
@@ -3540,9 +3613,9 @@ protected function matchEscapeCharacterInSelector(&$out, $keepEscapedNumber = fa
      * }}
      *
      * @param array          $out
-     * @param string|boolean $subSelector
+     * @param string|bool $subSelector
      *
-     * @return boolean
+     * @return bool
      */
     protected function selectorSingle(&$out, $subSelector = false)
     {
@@ -3766,7 +3839,7 @@ protected function selectorSingle(&$out, $subSelector = false)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function variable(&$out)
     {
@@ -3793,11 +3866,11 @@ protected function variable(&$out)
     /**
      * Parse a keyword
      *
-     * @param string  $word
-     * @param boolean $eatWhitespace
-     * @param boolean $inSelector
+     * @param string $word
+     * @param bool   $eatWhitespace
+     * @param bool   $inSelector
      *
-     * @return boolean
+     * @return bool
      */
     protected function keyword(&$word, $eatWhitespace = null, $inSelector = false)
     {
@@ -3860,11 +3933,11 @@ protected function keyword(&$word, $eatWhitespace = null, $inSelector = false)
     /**
      * Parse a keyword that should not start with a number
      *
-     * @param string  $word
-     * @param boolean $eatWhitespace
-     * @param boolean $inSelector
+     * @param string $word
+     * @param bool   $eatWhitespace
+     * @param bool   $inSelector
      *
-     * @return boolean
+     * @return bool
      */
     protected function restrictedKeyword(&$word, $eatWhitespace = null, $inSelector = false)
     {
@@ -3884,7 +3957,7 @@ protected function restrictedKeyword(&$word, $eatWhitespace = null, $inSelector
      *
      * @param string|array $placeholder
      *
-     * @return boolean
+     * @return bool
      */
     protected function placeholder(&$placeholder)
     {
@@ -3913,7 +3986,7 @@ protected function placeholder(&$placeholder)
      *
      * @param array $out
      *
-     * @return boolean
+     * @return bool
      */
     protected function url(&$out)
     {
@@ -3948,7 +4021,7 @@ protected function url(&$out)
      * Consume an end of statement delimiter
      * @param bool $eatWhitespace
      *
-     * @return boolean
+     * @return bool
      */
     protected function end($eatWhitespace = null)
     {
@@ -3969,7 +4042,7 @@ protected function end($eatWhitespace = null)
      *
      * @param array $value
      *
-     * @return array
+     * @return string[]
      */
     protected function stripAssignmentFlags(&$value)
     {
@@ -3996,7 +4069,7 @@ protected function stripAssignmentFlags(&$value)
      *
      * @param array $selectors
      *
-     * @return string
+     * @return bool
      */
     protected function stripOptionalFlag(&$selectors)
     {
@@ -4045,6 +4118,8 @@ private function pregQuote($what)
      * Extract line numbers from buffer
      *
      * @param string $buffer
+     *
+     * @return void
      */
     private function extractLineNumbers($buffer)
     {
@@ -4066,9 +4141,10 @@ private function extractLineNumbers($buffer)
     /**
      * Get source line number and column (given character position in the buffer)
      *
-     * @param integer $pos
+     * @param int $pos
      *
      * @return array
+     * @phpstan-return array{int, int}
      */
     private function getSourcePosition($pos)
     {
diff --git a/Contrib/scssphp/src/SourceMap/Base64.php b/Contrib/scssphp/src/SourceMap/Base64.php
index 4a5ed8bb..00b6b454 100644
--- a/Contrib/scssphp/src/SourceMap/Base64.php
+++ b/Contrib/scssphp/src/SourceMap/Base64.php
@@ -164,7 +164,7 @@ class Base64
     /**
      * Convert to base64
      *
-     * @param integer $value
+     * @param int $value
      *
      * @return string
      */
@@ -178,7 +178,7 @@ public static function encode($value)
      *
      * @param string $value
      *
-     * @return integer
+     * @return int
      */
     public static function decode($value)
     {
diff --git a/Contrib/scssphp/src/SourceMap/Base64VLQ.php b/Contrib/scssphp/src/SourceMap/Base64VLQ.php
index d47b96a1..2a5210c6 100644
--- a/Contrib/scssphp/src/SourceMap/Base64VLQ.php
+++ b/Contrib/scssphp/src/SourceMap/Base64VLQ.php
@@ -51,7 +51,7 @@ class Base64VLQ
     /**
      * Returns the VLQ encoded value.
      *
-     * @param integer $value
+     * @param int $value
      *
      * @return string
      */
@@ -80,9 +80,9 @@ public static function encode($value)
      * Decodes VLQValue.
      *
      * @param string $str
-     * @param integer $index
+     * @param int    $index
      *
-     * @return integer
+     * @return int
      */
     public static function decode($str, &$index)
     {
@@ -107,9 +107,9 @@ public static function decode($str, &$index)
      *   1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
      *   2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
      *
-     * @param integer $value
+     * @param int $value
      *
-     * @return integer
+     * @return int
      */
     private static function toVLQSigned($value)
     {
@@ -126,9 +126,9 @@ private static function toVLQSigned($value)
      *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
      *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
      *
-     * @param integer $value
+     * @param int $value
      *
-     * @return integer
+     * @return int
      */
     private static function fromVLQSigned($value)
     {
diff --git a/Contrib/scssphp/src/SourceMap/SourceMapGenerator.php b/Contrib/scssphp/src/SourceMap/SourceMapGenerator.php
index 2da902d4..ccd4f026 100644
--- a/Contrib/scssphp/src/SourceMap/SourceMapGenerator.php
+++ b/Contrib/scssphp/src/SourceMap/SourceMapGenerator.php
@@ -107,18 +107,18 @@ class SourceMapGenerator
      */
     public function __construct(array $options = [])
     {
-        $this->options = array_merge($this->defaultOptions, $options);
+        $this->options = array_replace($this->defaultOptions, $options);
         $this->encoder = new Base64VLQ();
     }
 
     /**
      * Adds a mapping
      *
-     * @param integer $generatedLine   The line number in generated file
-     * @param integer $generatedColumn The column number in generated file
-     * @param integer $originalLine    The line number in original file
-     * @param integer $originalColumn  The column number in original file
-     * @param string  $sourceFile      The original source file
+     * @param int    $generatedLine   The line number in generated file
+     * @param int    $generatedColumn The column number in generated file
+     * @param int    $originalLine    The line number in original file
+     * @param int    $originalColumn  The column number in original file
+     * @param string $sourceFile      The original source file
      *
      * @return void
      */
@@ -140,7 +140,7 @@ public function addMapping($generatedLine, $generatedColumn, $originalLine, $ori
      *
      * @param string $content The content to write
      *
-     * @return string
+     * @return string|null
      *
      * @throws \ScssPhp\ScssPhp\Exception\CompilerException If the file could not be saved
      * @deprecated
@@ -148,6 +148,7 @@ public function addMapping($generatedLine, $generatedColumn, $originalLine, $ori
     public function saveMap($content)
     {
         $file = $this->options['sourceMapWriteTo'];
+        assert($file !== null);
         $dir  = \dirname($file);
 
         // directory does not exist
@@ -201,7 +202,7 @@ public function generateJson($prefix = '')
         // A list of original sources used by the 'mappings' entry.
         $sourceMap['sources'] = [];
 
-        foreach ($this->sources as $sourceUri => $sourceFilename) {
+        foreach ($this->sources as $sourceFilename) {
             $sourceMap['sources'][] = $this->normalizeFilename($sourceFilename);
         }
 
@@ -223,7 +224,15 @@ public function generateJson($prefix = '')
             unset($sourceMap['sourceRoot']);
         }
 
-        return json_encode($sourceMap, JSON_UNESCAPED_SLASHES);
+        $jsonSourceMap = json_encode($sourceMap, JSON_UNESCAPED_SLASHES);
+
+        if (json_last_error() !== JSON_ERROR_NONE) {
+            throw new \RuntimeException(json_last_error_msg());
+        }
+
+        assert($jsonSourceMap !== false);
+
+        return $jsonSourceMap;
     }
 
     /**
@@ -240,7 +249,7 @@ protected function getSourcesContent()
         $content = [];
 
         foreach ($this->sources as $sourceFile) {
-            $content[] = @file_get_contents($sourceFile);
+            $content[] = file_get_contents($sourceFile);
         }
 
         return $content;
@@ -326,7 +335,7 @@ public function generateMappings($prefix = '')
      *
      * @param string $filename
      *
-     * @return integer|false
+     * @return int|false
      */
     protected function findFileIndex($filename)
     {
@@ -362,8 +371,8 @@ protected function normalizeFilename($filename)
     /**
      * Fix windows paths
      *
-     * @param string  $path
-     * @param boolean $addEndSlash
+     * @param string $path
+     * @param bool   $addEndSlash
      *
      * @return string
      */
diff --git a/Contrib/scssphp/src/Type.php b/Contrib/scssphp/src/Type.php
index fb2a1d7f..d4308878 100644
--- a/Contrib/scssphp/src/Type.php
+++ b/Contrib/scssphp/src/Type.php
@@ -19,58 +19,190 @@
  */
 class Type
 {
+    /**
+     * @internal
+     */
     const T_ASSIGN = 'assign';
+    /**
+     * @internal
+     */
     const T_AT_ROOT = 'at-root';
+    /**
+     * @internal
+     */
     const T_BLOCK = 'block';
-    /** @deprecated */
+    /**
+     * @deprecated
+     * @internal
+     */
     const T_BREAK = 'break';
+    /**
+     * @internal
+     */
     const T_CHARSET = 'charset';
     const T_COLOR = 'color';
+    /**
+     * @internal
+     */
     const T_COMMENT = 'comment';
-    /** @deprecated */
+    /**
+     * @deprecated
+     * @internal
+     */
     const T_CONTINUE = 'continue';
-    /** @deprecated */
+    /**
+     * @deprecated
+     * @internal
+     */
     const T_CONTROL = 'control';
+    /**
+     * @internal
+     */
     const T_CUSTOM_PROPERTY = 'custom';
+    /**
+     * @internal
+     */
     const T_DEBUG = 'debug';
+    /**
+     * @internal
+     */
     const T_DIRECTIVE = 'directive';
+    /**
+     * @internal
+     */
     const T_EACH = 'each';
+    /**
+     * @internal
+     */
     const T_ELSE = 'else';
+    /**
+     * @internal
+     */
     const T_ELSEIF = 'elseif';
+    /**
+     * @internal
+     */
     const T_ERROR = 'error';
+    /**
+     * @internal
+     */
     const T_EXPRESSION = 'exp';
+    /**
+     * @internal
+     */
     const T_EXTEND = 'extend';
+    /**
+     * @internal
+     */
     const T_FOR = 'for';
     const T_FUNCTION = 'function';
+    /**
+     * @internal
+     */
     const T_FUNCTION_REFERENCE = 'function-reference';
+    /**
+     * @internal
+     */
     const T_FUNCTION_CALL = 'fncall';
+    /**
+     * @internal
+     */
     const T_HSL = 'hsl';
+    /**
+     * @internal
+     */
     const T_HWB = 'hwb';
+    /**
+     * @internal
+     */
     const T_IF = 'if';
+    /**
+     * @internal
+     */
     const T_IMPORT = 'import';
+    /**
+     * @internal
+     */
     const T_INCLUDE = 'include';
+    /**
+     * @internal
+     */
     const T_INTERPOLATE = 'interpolate';
+    /**
+     * @internal
+     */
     const T_INTERPOLATED = 'interpolated';
+    /**
+     * @internal
+     */
     const T_KEYWORD = 'keyword';
     const T_LIST = 'list';
     const T_MAP = 'map';
+    /**
+     * @internal
+     */
     const T_MEDIA = 'media';
+    /**
+     * @internal
+     */
     const T_MEDIA_EXPRESSION = 'mediaExp';
+    /**
+     * @internal
+     */
     const T_MEDIA_TYPE = 'mediaType';
+    /**
+     * @internal
+     */
     const T_MEDIA_VALUE = 'mediaValue';
+    /**
+     * @internal
+     */
     const T_MIXIN = 'mixin';
+    /**
+     * @internal
+     */
     const T_MIXIN_CONTENT = 'mixin_content';
+    /**
+     * @internal
+     */
     const T_NESTED_PROPERTY = 'nestedprop';
+    /**
+     * @internal
+     */
     const T_NOT = 'not';
     const T_NULL = 'null';
     const T_NUMBER = 'number';
+    /**
+     * @internal
+     */
     const T_RETURN = 'return';
+    /**
+     * @internal
+     */
     const T_ROOT = 'root';
+    /**
+     * @internal
+     */
     const T_SCSSPHP_IMPORT_ONCE = 'scssphp-import-once';
+    /**
+     * @internal
+     */
     const T_SELF = 'self';
     const T_STRING = 'string';
+    /**
+     * @internal
+     */
     const T_UNARY = 'unary';
+    /**
+     * @internal
+     */
     const T_VARIABLE = 'var';
+    /**
+     * @internal
+     */
     const T_WARN = 'warn';
+    /**
+     * @internal
+     */
     const T_WHILE = 'while';
 }
diff --git a/Contrib/scssphp/src/Util.php b/Contrib/scssphp/src/Util.php
index 62cd2a20..ad608ceb 100644
--- a/Contrib/scssphp/src/Util.php
+++ b/Contrib/scssphp/src/Util.php
@@ -79,7 +79,7 @@ public static function encodeURIComponent($string)
     /**
      * mb_chr() wrapper
      *
-     * @param integer $code
+     * @param int $code
      *
      * @return string
      */
diff --git a/Contrib/scssphp/src/Version.php b/Contrib/scssphp/src/Version.php
index 62c8006a..d604a505 100644
--- a/Contrib/scssphp/src/Version.php
+++ b/Contrib/scssphp/src/Version.php
@@ -19,5 +19,5 @@
  */
 class Version
 {
-    const VERSION = '1.8.1';
+    const VERSION = '1.11.0';
 }
diff --git a/Resources/Private/Extensions/felogin/Resources/Private/Templates/Login/Login.html b/Resources/Private/Extensions/felogin/Resources/Private/Templates/Login/Login.html
index 654f17de..b5d2f3d6 100644
--- a/Resources/Private/Extensions/felogin/Resources/Private/Templates/Login/Login.html
+++ b/Resources/Private/Extensions/felogin/Resources/Private/Templates/Login/Login.html
@@ -1,51 +1,47 @@
 
+
 
-
-	
-
+
 
-
-
-
-	
-		
-			
-		
-	
-	
-		
-			
-		
-	
 
+
+
+	
+
+
 
-	
+	
 		
 	
 
+
 
 	
- - +
- - +
+
-
-
- + +
+
+
- @@ -78,8 +75,6 @@

- - {extraHidden}

diff --git a/Resources/Private/Extensions/news/Configuration/TypoScript/setup.typoscript b/Resources/Private/Extensions/news/Configuration/TypoScript/setup.typoscript index a928048d..94c47163 100644 --- a/Resources/Private/Extensions/news/Configuration/TypoScript/setup.typoscript +++ b/Resources/Private/Extensions/news/Configuration/TypoScript/setup.typoscript @@ -27,7 +27,6 @@ plugin.tx_news { srcsetTablet = {$bootstrap.image.srcsetTablet} srcsetMobile = {$bootstrap.image.srcsetMobile} imgCopyright = {$bootstrap.extconf.imgCopyright} - typoscriptRendering = {$bootstrap.ext.typoscriptRendering} lazyLoad = {$bootstrap.extconf.lazyLoad} picturefill = {$bootstrap.image.picturefill} magnifying = {$bootstrap.config.magnifying} diff --git a/Resources/Private/Partials/Content/Card/TilingCard.html b/Resources/Private/Partials/Content/Card/TilingCard.html new file mode 100644 index 00000000..3e2d5557 --- /dev/null +++ b/Resources/Private/Partials/Content/Card/TilingCard.html @@ -0,0 +1,94 @@ + +Tiling Card + + +.card.tiling {box-shadow: none;} +.card.tiling .background-image {min-height: 300px;} +@media (max-width: 991.98px) { + .odd .tiling-first-right {order: 2;} + .odd .tiling-second-right {order: 1;} + .even .tiling-first-left {order: 2;} + .even .tiling-second-left {order: 1;} +} +@media (max-width: 767.98px) { + .odd .tiling-first-right {order: 1;} + .odd .tiling-second-right {order: 2;} + .odd .tiling-first-left {order: 2;} + .odd .tiling-second-left {order: 1;} + .tiling {margin-bottom: 3rem;} +} + + +
+ + +
+ +
+
+ +
+
+ +
+ +
+
+ +
+
+
+
+ + + + + +
+ + + + + + {data.bodytext -> f:format.crop(maxCharacters: '{card.cropMaxCharacters}', respectWordBoundaries:'1') -> f:format.html()} + + + {data.bodytext -> f:format.html()} + + +
+ + +
+ {data.tx_t3sbootstrap_cardfooter -> f:format.raw()} + + + + +
+ + + +
+
+
+
+ +
+ + + + + + + + {card.button.text -> f:format.raw()} + + + {f:translate(key: 'LLL:EXT:t3sbootstrap/Resources/Private/Language/locallang.xlf:more')} + + + + + diff --git a/Resources/Private/Partials/Content/Media/Gallery.html b/Resources/Private/Partials/Content/Media/Gallery.html index c3f7d0c4..e9df0149 100644 --- a/Resources/Private/Partials/Content/Media/Gallery.html +++ b/Resources/Private/Partials/Content/Media/Gallery.html @@ -57,7 +57,14 @@
- /{posters.{rowiterator.index}.poster} + + + /{posters.{rowiterator.index}.poster} + + + + +
- + diff --git a/Resources/Private/Partials/Content/Media/Type/Video.html b/Resources/Private/Partials/Content/Media/Type/Video.html index 45cfe9e6..fede0c8f 100644 --- a/Resources/Private/Partials/Content/Media/Type/Video.html +++ b/Resources/Private/Partials/Content/Media/Type/Video.html @@ -1,6 +1,11 @@
-
+ + +{addmedia.ratioCalcCss -> f:format.raw()} + + +
diff --git a/Resources/Private/Partials/Content/Swiper/Default.html b/Resources/Private/Partials/Content/Swiper/Default.html index 87b04b91..46738d79 100644 --- a/Resources/Private/Partials/Content/Swiper/Default.html +++ b/Resources/Private/Partials/Content/Swiper/Default.html @@ -43,59 +43,16 @@ #swiper-{data.uid}{width:100%;height:auto}#swiper-{data.uid} .swiper-wrapper{height:auto}#swiper-{data.uid} .swiper-slide{flex-direction:column;display:-webkit-box;display:-ms-flexbox;display:-webkit-flex;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;-webkit-justify-content:start;justify-content:start;-webkit-box-align:stretch;-ms-flex-align:stretch;-webkit-align-items:stretch;align-items:stretch;height:auto !important}#swiper-{data.uid} .swiper-slide img{display:block;width:100%;height:100%;object-fit:cover}#swiper-{data.uid} .swiper-button-next,#swiper-{data.uid} .swiper-button-prev{color:#fff;text-shadow:1px 1px 2px #000,0 0 25px #000,0 0 5px #000} - - // Swiper Slider - Partials/Content/Swiper/Default.html - var spv = {slidesPerView}, - spb = {spaceBetween}, - spg = {slidesPerGroup}, - del = {delay}, - b576 = {breakpoints576}, - b768 = {breakpoints768}, - b992 = {breakpoints992}, - lop = {loop}, - suid = {data.uid}; - var swiper = new Swiper('#swiper-{data.uid}', { - slidesPerView: spv, - spaceBetween: spb, - slidesPerGroup: spg, - loop: lop, - disableOnInteraction: false, - pagination: { - el: ".swiper-pagination", - clickable: true, - }, - autoplay: { - delay: del, - }, - navigation: { - nextEl: '.next-'+suid, - prevEl: '.prev-'+suid, - }, - breakpoints: { - 10: { - slidesPerView: 1, - }, - 576: { - slidesPerView: b576, - }, - 768: { - slidesPerView: b768, - }, - 992: { - slidesPerView: b992, - }, - }, - }); - - var swi = document.getElementById('swiper-{data.uid}'), - pag = swi.querySelector('.swiper-pagination'), - next = swi.getElementsByClassName('swiper-button-next'), - prev = swi.getElementsByClassName('swiper-button-prev'), - offset = pag.clientHeight - 5; - next[0].style.top = 'calc(50% - '+offset+'px)'; - prev[0].style.top = 'calc(50% - '+offset+'px)'; + + + + {customSwiperJs} + + + + + -
f:format.raw()}>