- {if $__contentInteractionPagination}
+{if $contentInteractionTabsComponent|isset || $__contentInteractionPagination || $__contentInteractionButtons || $__contentInteractionDropdownItems || $__contentInteractionShareButton}
+
+ {if $contentInteractionTabsComponent|isset}
+ {unsafe:$contentInteractionTabsComponent->render()}
+ {elseif $__contentInteractionPagination}
diff --git a/com.woltlab.wcf/templates/shared_contentInteractionTabs.tpl b/com.woltlab.wcf/templates/shared_contentInteractionTabs.tpl
new file mode 100644
index 00000000000..334857099fd
--- /dev/null
+++ b/com.woltlab.wcf/templates/shared_contentInteractionTabs.tpl
@@ -0,0 +1,13 @@
+
diff --git a/wcfsetup/install/files/lib/system/view/component/ContentInteractionTabsComponent.class.php b/wcfsetup/install/files/lib/system/view/component/ContentInteractionTabsComponent.class.php
new file mode 100644
index 00000000000..a5b91adc510
--- /dev/null
+++ b/wcfsetup/install/files/lib/system/view/component/ContentInteractionTabsComponent.class.php
@@ -0,0 +1,60 @@
+
+ * @since 6.2
+ */
+final class ContentInteractionTabsComponent
+{
+ /**
+ * @var list
+ */
+ private array $tabs = [];
+
+ public function addTab(string $title, string $link, bool $active = false): void
+ {
+ if ($active && $this->getActiveTab() !== null) {
+ throw new \BadMethodCallException("The tab '{$this->getActiveTab()->link}' is already marked as active");
+ }
+
+ $this->tabs[] = new ContentInteractionTab($title, $link, $active);
+ }
+
+ private function getActiveTab(): ?ContentInteractionTab
+ {
+ return \array_find($this->tabs, static fn($tab) => $tab->active);
+ }
+
+ public function render(): string
+ {
+ if (!$this->tabs === []) {
+ return '';
+ }
+
+ return WCF::getTPL()->render(
+ 'wcf',
+ 'shared_contentInteractionTabs',
+ [
+ 'tabs' => $this->tabs,
+ ],
+ );
+ }
+}
+
+/** @internal */
+final class ContentInteractionTab
+{
+ public function __construct(
+ public readonly string $title,
+ public readonly string $link,
+ public readonly bool $active,
+ ) {}
+}
diff --git a/wcfsetup/install/files/style/layout/content.scss b/wcfsetup/install/files/style/layout/content.scss
index b2c81fe6146..bf81fe134b2 100644
--- a/wcfsetup/install/files/style/layout/content.scss
+++ b/wcfsetup/install/files/style/layout/content.scss
@@ -424,12 +424,7 @@ fieldset {
.contentInteraction {
display: flex;
- justify-content: space-between;
margin-top: 20px;
-
- @include screen-xs {
- flex-wrap: wrap;
- }
}
.contentInteractionPagination {
@@ -520,6 +515,9 @@ fieldset {
}
.content {
+ container-type: inline-size;
+ container-name: content;
+
.contentInteraction + .section,
.contentInteraction + form {
margin-top: 20px;
@@ -571,3 +569,61 @@ fieldset {
}
}
}
+
+.contentInteraction--withTabs {
+ border-bottom: solid 1px var(--wcfContentBorderInner);
+}
+
+.contentInteractionTabs {
+ display: flex;
+ gap: 10px;
+}
+
+.contentInteractionTab {
+ display: flex;
+ white-space: nowrap;
+}
+
+.contentInteractionTab__link {
+ display: flex;
+ padding: 5px 10px;
+ color: var(--wcfContentDimmedText);
+ font-size: var(--wcfFontSizeSection);
+ font-weight: 600;
+ position: relative;
+}
+
+.contentInteractionTab__link::after {
+ content: "";
+ left: 0;
+ right: 0;
+ bottom: -1px;
+ position: absolute;
+ height: 3px;
+}
+
+.contentInteractionTab--active .contentInteractionTab__link {
+ color: var(--wcfContentText);
+}
+
+.contentInteractionTab__link:hover {
+ color: var(--wcfContentText);
+}
+
+.contentInteractionTab__link:hover::after {
+ background-color: var(--wcfContentBorderInner);
+}
+
+.contentInteractionTab--active .contentInteractionTab__link::after {
+ background-color: var(--wcfTabularBoxHeadline);
+}
+
+@container content (width < 800px) {
+ .contentInteraction {
+ flex-direction: column;
+ }
+
+ .contentInteractionButtonContainer {
+ order: -1;
+ }
+}