diff --git a/citizen.php b/citizen.php
new file mode 100644
index 00000000..71ef8d1b
--- /dev/null
+++ b/citizen.php
@@ -0,0 +1,14 @@
+get( 'headelement' );
+ $loggedinclass = 'not-logged';
+
+ // Add class if logged in
+ if ( $this->getSkin()->getUser()->isLoggedIn() ) {
+ $loggedinclass .= 'logged-in';
+ }
+
+ $html .= Html::rawElement( 'div', [ 'class' => $loggedinclass, 'id' => 'mw-wrapper' ],
+ // Header
+ Html::rawElement( 'header', [ 'class' => 'mw-header-container', 'id' => 'mw-navigation' ],
+ Html::rawElement( 'div', [ 'class' => 'mw-header-icons'],
+ // Site navigation menu
+ $this->getHamburgerMenu()
+ ) .
+ Html::rawElement( 'div', [ 'class' => 'mw-header-icons'],
+ // User icons
+ Html::rawElement( 'div', [ 'class' => 'mw-header', 'id' => 'user-icons' ],
+ $this->getUserIcons()
+ ) .
+ // Search bar
+ $this->getSearchButton()
+ )
+ ) .
+ // Main body
+ Html::rawElement( 'main', [ 'class' => 'mw-body', 'id' => 'content', 'role' => 'main' ],
+ // Container for compatiblity with extensions
+ Html::rawElement( 'section', [ 'id' => 'mw-body-container' ],
+ $this->getSiteNotice() .
+ $this->getNewTalk() .
+ $this->getIndicators() .
+ // Page editing and tools
+ $this->getPageTools() .
+ Html::rawElement( 'h1',
+ [
+ 'class' => 'firstHeading',
+ 'lang' => $this->get( 'pageLanguage' )
+ ],
+ $this->get( 'title' )
+ ) .
+ Html::rawElement( 'div', [ 'id' => 'siteSub' ],
+ $this->getMsg( 'tagline' )->parse()
+ ) .
+ Html::rawElement( 'div', [ 'class' => 'mw-body-content' ],
+ Html::rawElement( 'div', [ 'id' => 'contentSub' ],
+ $this->getPageSubtitle() .
+ Html::rawElement(
+ 'p',
+ [],
+ $this->get( 'undelete' )
+ )
+ ) .
+ $this->get( 'bodycontent' ) .
+ $this->getClear() .
+ Html::rawElement( 'div', [ 'class' => 'printfooter' ],
+ $this->get( 'printfooter' )
+ ) .
+ $this->getPageLinks() .
+ $this->getCategoryLinks()
+ ) .
+ $this->getDataAfterContent() .
+ $this->get( 'debughtml' )
+ )
+ ) .
+ $this->getFooterBlock() .
+ // Site title for sidebar
+ Html::rawElement( 'div', [ 'id' => 'mw-sidebar-sitename', 'role' => 'banner' ],
+ $this->getSiteTitle('link')
+ ) .
+ $this->getBottomBar()
+ );
+
+ $html .= $this->getTrail();
+ $html .= Html::closeElement( 'body' );
+ $html .= Html::closeElement( 'html' );
+
+ echo $html;
+ }
+
+ /**
+ * Generates the bottom bar
+ * @return string html
+ */
+ protected function getBottomBar() {
+
+ $linkDiscord = 'https://discord.gg/3kjftWK';
+ $titleDiscord = 'Contact Us on Discord';
+ $textDiscord = 'Discord';
+ /*
+ $linkReddit = 'https://www.reddit.com/r/starcitizen';
+ $titleReddit = 'Visit /r/starcitizen';
+ $textReddit = 'Reddit';
+ */
+
+ $html = Html::openElement( 'div', [ 'id' => 'mw-bottombar' ] );
+
+ $html .= Html::rawElement( 'div', [ 'id' => 'mw-bottombar-contact' ],
+ Html::rawElement( 'div', [ 'class' => 'citizen-ui-icon', 'id' => 'citizen-ui-discord' ],
+ Html::rawElement( 'a', [ 'href' => $linkDiscord, 'title' => $titleDiscord, 'rel' => 'noopener noreferrer', 'target' => '_blank' ], $textDiscord )
+ )
+ /*
+ Html::rawElement( 'div', [ 'class' => 'citizen-ui-icon', 'id' => 'citizen-ui-reddit' ],
+ Html::rawElement( 'a', [ 'href' => $linkReddit, 'title' => $titleReddit, 'target' => '_blank' ], $textReddit )
+ )
+ */
+ );
+ $html .= Html::closeElement( 'div' );
+
+ return $html;
+ }
+
+ /**
+ * Generates the search button
+ * @return string html
+ */
+ protected function getSearchButton() {
+ $titleButton = 'Toggle Search';
+
+ $html = Html::rawElement( 'div', [ 'class' => 'mw-header-end', 'id' => 'site-search' ],
+ Html::rawElement( 'input', [ 'type' => 'checkbox', 'role' => 'button', 'title' => $titleButton ]) .
+
+ // Search button
+ Html::rawElement( 'div', [ 'id' => 'search-icon-container' ],
+ Html::rawElement( 'div', [ 'id' => 'search-icon' ] )
+ ) .
+
+ // Search form
+ $this->getSearch()
+ );
+ return $html;
+ }
+
+ /**
+ * Generates the hamburger menu
+ * @return string html
+ */
+ protected function getHamburgerMenu() {
+ $titleButton = 'Toggle Menu';
+
+ $html = Html::openElement( 'div', [ 'class' => 'mw-header-end', 'id' => 'mw-header-menu' ]);
+ $html .= Html::rawElement( 'input', [ 'type' => 'checkbox', 'role' => 'button', 'title' => $titleButton ]);
+
+ // Actual hamburger
+ $html .= Html::openElement( 'div', [ 'id' => 'mw-header-menu-toggle' ]);
+ for ($i = 1; $i <= 3; $i++) {
+ $html .= Html::rawElement( 'span' );
+ }
+ $html .= Html::closeElement( 'div' );
+ // Get sidebar links
+ $html .= Html::rawElement( 'div', [ 'id' => 'mw-header-menu-drawer' ],
+ Html::rawElement( 'div', [ 'id' => 'mw-header-menu-drawer-container' ],
+ $this->getSiteTitle('text') .
+ // Container for navigation and tools
+ Html::rawElement( 'div', [ 'id' => 'p-nt-container' ],
+ $this->getSiteNavigation()
+ ) .
+ $this->getUserLinks()
+ )
+ );
+ $html .= Html::closeElement( 'div' );
+
+ return $html;
+ }
+
+ /**
+ * Generates the sitetitle
+ * @return string html
+ */
+ protected function getSiteTitle( $option ) {
+ $html = '';
+ $language = $this->getSkin()->getLanguage();
+ $siteTitle = $language->convert( $this->getMsg( 'sitetitle' )->escaped() );
+
+ switch ( $option ) {
+ case 'link':
+ $html .= Html::rawElement(
+ 'a',
+ [
+ 'id' => 'p-banner',
+ 'class' => 'mw-wiki-title',
+ 'href' => $this->data['nav_urls']['mainpage']['href']
+ ] + Linker::tooltipAndAccesskeyAttribs( 'p-logo' ),
+ $siteTitle
+ );
+ break;
+ case 'text':
+ $html .= Html::rawElement(
+ 'span',
+ [
+ 'id' => 'p-banner',
+ 'class' => 'mw-wiki-title',
+ ],
+ $siteTitle
+ );
+ break;
+ }
+
+ return $html;
+ }
+
+ /**
+ * Generates the description for footer
+ * @return string html
+ */
+ protected function getFooterDesc() {
+ $html = '';
+ $language = $this->getSkin()->getLanguage();
+ $siteDesc = $language->convert( $this->getMsg( 'citizen-footer-desc' )->escaped() );
+
+ $html .= Html::rawElement(
+ 'span',
+ [
+ 'id' => 'mw-footer-desc'
+ ],
+ $siteDesc
+ );
+
+ return $html;
+ }
+
+ /**
+ * Generates the tagline for footer
+ * @return string html
+ */
+ protected function getFooterTagline() {
+ $html = '';
+ $language = $this->getSkin()->getLanguage();
+ $siteTagline = $language->convert( $this->getMsg( 'citizen-footer-tagline' )->escaped() );
+
+ $html .= Html::rawElement(
+ 'span',
+ [
+ 'id' => 'mw-footer-tagline'
+ ],
+ $siteTagline
+ );
+
+ return $html;
+ }
+
+ /**
+ * Generates the logo
+ * @param string $id
+ *
+ * @return string html
+ */
+ protected function getLogo( $id = 'p-logo' ) {
+ $html = Html::openElement(
+ 'div',
+ [
+ 'id' => $id,
+ 'class' => 'mw-portlet',
+ 'role' => 'banner'
+ ]
+ );
+ $html .= Html::element(
+ 'a',
+ [
+ 'href' => $this->data['nav_urls']['mainpage']['href'],
+ 'class' => 'mw-wiki-logo',
+ ] + Linker::tooltipAndAccesskeyAttribs( 'p-logo' )
+ );
+ $html .= Html::closeElement( 'div' );
+
+ return $html;
+ }
+
+ /**
+ * Generates the search form
+ * @return string html
+ */
+ protected function getSearch() {
+ $html = Html::openElement(
+ 'form',
+ [
+ 'action' => $this->get( 'wgScript' ),
+ 'role' => 'search',
+ 'class' => 'mw-portlet',
+ 'id' => 'p-search'
+ ]
+ );
+ $html .= Html::hidden( 'title', $this->get( 'searchtitle' ) );
+ $html .= Html::rawElement(
+ 'h3',
+ [],
+ Html::label( $this->getMsg( 'search' )->text(), 'searchInput' )
+ );
+ $html .= $this->makeSearchInput( [ 'id' => 'searchInput' ] );
+ $html .= $this->makeSearchButton( 'go', [ 'id' => 'searchGoButton', 'class' => 'searchButton' ] );
+ $html .= Html::closeElement( 'form' );
+
+ return $html;
+ }
+
+ /**
+ * Generates the sidebar
+ * Set the elements to true to allow them to be part of the sidebar
+ * Or get rid of this entirely, and take the specific bits to use wherever you actually want them
+ * * Toolbox is the page/site tools that appears under the sidebar in vector
+ * * Languages is the interlanguage links on the page via en:... es:... etc
+ * * Default is each user-specified box as defined on MediaWiki:Sidebar; you will still need a foreach loop
+ * to parse these.
+ * @return string html
+ */
+ protected function getSiteNavigation() {
+ $html = '';
+
+ $sidebar = $this->getSidebar();
+ $sidebar['SEARCH'] = false;
+ $sidebar['TOOLBOX'] = true;
+ $sidebar['LANGUAGES'] = false;
+
+ foreach ( $sidebar as $name => $content ) {
+ if ( $content === false ) {
+ continue;
+ }
+ // Numeric strings gets an integer when set as key, cast back - T73639
+ $name = (string)$name;
+
+ switch ( $name ) {
+ case 'SEARCH':
+ $html .= $this->getSearch();
+ break;
+ case 'TOOLBOX':
+ $html .= $this->getPortlet( 'tb', $this->getToolbox(), 'toolbox' );
+ break;
+ case 'LANGUAGES':
+ $html .= $this->getLanguageLinks();
+ break;
+ default:
+ $html .= $this->getPortlet( $name, $content['content'] );
+ break;
+ }
+ }
+ return $html;
+ }
+ /**
+ * Generates user icon bar
+ * @return string html
+ */
+ protected function getUserIcons() {
+
+ $personalTools = $this->getPersonalTools();
+ $html = '';
+
+ // Create the Echo badges and ULS
+ $extraTools = [];
+ if ( isset( $personalTools['notifications-alert'] ) ) {
+ $extraTools['notifications-alert'] = $personalTools['notifications-alert'];
+ }
+ if ( isset( $personalTools['notifications-notice'] ) ) {
+ $extraTools['notifications-notice'] = $personalTools['notifications-notice'];
+ }
+ if ( isset( $personalTools['uls'] ) ) {
+ $extraTools['uls'] = $personalTools['uls'];
+ }
+
+ // Place the extra icons/outside stuff
+ if ( !empty( $extraTools ) ) {
+ $iconList = '';
+ foreach ( $extraTools as $key => $item ) {
+ $iconList .= $this->makeListItem( $key, $item );
+ }
+
+ $html .= Html::rawElement(
+ 'div',
+ [ 'id' => 'p-personal-extra', 'class' => 'p-body' ],
+ Html::rawElement( 'ul', [], $iconList )
+ );
+ }
+ return $html;
+ }
+
+ /**
+ * Generates user tools menu
+ * @return string html
+ */
+ protected function getUserLinks() {
+
+ $personalTools = $this->getPersonalTools();
+
+ $html = '';
+
+ // Move the Echo badges and ULS out of default list
+ $extraTools = [];
+ if ( isset( $personalTools['notifications-alert'] ) ) {
+ unset( $personalTools['notifications-alert'] );
+ }
+ if ( isset( $personalTools['notifications-notice'] ) ) {
+ unset( $personalTools['notifications-notice'] );
+ }
+ if ( isset( $personalTools['uls'] ) ) {
+ unset( $personalTools['uls'] );
+ }
+
+ $html .= Html::openElement( 'div', [ 'id' => 'mw-user-links' ] );
+ $html .= $this->getPortlet( 'personal', $personalTools, 'personaltools' );
+ $html .= Html::closeElement( 'div' );
+
+ return $html;
+ }
+
+ /**
+ * In other languages list
+ *
+ * @return string html
+ */
+ protected function getLanguageLinks() {
+ $html = '';
+ if ( $this->data['language_urls'] !== false ) {
+ $html .= $this->getPortlet( 'lang', $this->data['language_urls'], 'otherlanguages' );
+ }
+
+ return $html;
+ }
+
+ /**
+ * Language variants. Displays list for converting between different scripts in the same language,
+ * if using a language where this is applicable (such as latin vs cyric display for serbian).
+ *
+ * @return string html
+ */
+ protected function getVariants() {
+ $html = '';
+ if ( count( $this->data['content_navigation']['variants'] ) > 0 ) {
+ $html .= $this->getPortlet(
+ 'variants',
+ $this->data['content_navigation']['variants']
+ );
+ }
+
+ return $html;
+ }
+
+ /**
+ * Generates page-related tools
+ * @return string html
+ */
+ protected function getPageTools() {
+ $html = '';
+ // Only display if user is logged in
+ // TODO: Make it check for EDIT permission instead
+ if ( $this->getSkin()->getUser()->isLoggedIn() ) {
+
+ $html .= Html::openElement( 'div', [ 'class' => 'mw-side', 'id' => 'page-tools' ]);
+ // 'View' actions for the page: view, edit, view history, etc
+ $html .= $this->getPortlet(
+ 'views',
+ $this->data['content_navigation']['views']
+ );
+ // Other actions for the page: move, delete, protect, everything else
+ $html .= $this->getPortlet(
+ 'actions',
+ $this->data['content_navigation']['actions']
+ );
+ $html .= Html::closeElement( 'div' );
+ }
+ return $html;
+ }
+
+ /**
+ * Generates page-related links at the bottom
+ * @return string html
+ */
+ protected function getPageLinks() {
+ // Namespaces: links for 'content' and 'talk' for namespaces with talkpages. Otherwise is just the content.
+ // Usually rendered as tabs on the top of the page.
+ $html = $this->getPortlet(
+ 'namespaces',
+ $this->data['content_navigation']['namespaces']
+ );
+ // Language variant options
+ $html .= $this->getVariants();
+
+ return $html;
+ }
+
+ /**
+ * Generates siteNotice, if any
+ * @return string html
+ */
+ protected function getSiteNotice() {
+ return $this->getIfExists( 'sitenotice', [
+ 'wrapper' => 'div',
+ 'parameters' => [ 'id' => 'siteNotice' ]
+ ] );
+ }
+
+ /**
+ * Generates new talk message banner, if any
+ * @return string html
+ */
+ protected function getNewTalk() {
+ return $this->getIfExists( 'newtalk', [
+ 'wrapper' => 'div',
+ 'parameters' => [ 'class' => 'usermessage' ]
+ ] );
+ }
+
+ /**
+ * Generates subtitle stuff, if any
+ * @return string html
+ */
+ protected function getPageSubtitle() {
+ return $this->getIfExists( 'subtitle', [ 'wrapper' => 'p' ] );
+ }
+
+ /**
+ * Generates category links, if any
+ * @return string html
+ */
+ protected function getCategoryLinks() {
+ return $this->getIfExists( 'catlinks' );
+ }
+
+ /**
+ * Generates data after content stuff, if any
+ * @return string html
+ */
+ protected function getDataAfterContent() {
+ return $this->getIfExists( 'dataAfterContent' );
+ }
+
+ /**
+ * Simple wrapper for random if-statement-wrapped $this->data things
+ *
+ * @param string $object name of thing
+ * @param array $setOptions
+ *
+ * @return string html
+ */
+ protected function getIfExists( $object, $setOptions = [] ) {
+ $options = $setOptions + [
+ 'wrapper' => 'none',
+ 'parameters' => []
+ ];
+
+ $html = '';
+
+ if ( $this->data[$object] ) {
+ if ( $options['wrapper'] == 'none' ) {
+ $html .= $this->get( $object );
+ } else {
+ $html .= Html::rawElement(
+ $options['wrapper'],
+ $options['parameters'],
+ $this->get( $object )
+ );
+ }
+ }
+
+ return $html;
+ }
+
+ /**
+ * Generates a block of navigation links with a header
+ *
+ * @param string $name
+ * @param array|string $content array of links for use with makeListItem, or a block of text
+ * @param null|string|array $msg
+ * @param array $setOptions random crap to rename/do/whatever
+ *
+ * @return string html
+ */
+ protected function getPortlet( $name, $content, $msg = null, $setOptions = [] ) {
+ // random stuff to override with any provided options
+ $options = $setOptions + [
+ // extra classes/ids
+ 'id' => 'p-' . $name,
+ 'class' => 'mw-portlet',
+ 'extra-classes' => '',
+ // what to wrap the body list in, if anything
+ 'body-wrapper' => 'nav',
+ 'body-id' => null,
+ 'body-class' => 'mw-portlet-body',
+ // makeListItem options
+ 'list-item' => [ 'text-wrapper' => [ 'tag' => 'span' ] ],
+ // option to stick arbitrary stuff at the beginning of the ul
+ 'list-prepend' => '',
+ // old toolbox hook support (use: [ 'SkinTemplateToolboxEnd' => [ &$skin, true ] ])
+ 'hooks' => ''
+ ];
+
+ // Handle the different $msg possibilities
+ if ( $msg === null ) {
+ $msg = $name;
+ } elseif ( is_array( $msg ) ) {
+ $msgString = array_shift( $msg );
+ $msgParams = $msg;
+ $msg = $msgString;
+ }
+ $msgObj = $this->getMsg( $msg );
+ if ( $msgObj->exists() ) {
+ if ( isset( $msgParams ) && !empty( $msgParams ) ) {
+ $msgString = $this->getMsg( $msg, $msgParams )->parse();
+ } else {
+ $msgString = $msgObj->parse();
+ }
+ } else {
+ $msgString = htmlspecialchars( $msg );
+ }
+
+ $labelId = Sanitizer::escapeIdForAttribute( "p-$name-label" );
+
+ if ( is_array( $content ) ) {
+ $contentText = Html::openElement( 'ul',
+ [ 'lang' => $this->get( 'userlang' ), 'dir' => $this->get( 'dir' ) ]
+ );
+ $contentText .= $options['list-prepend'];
+ foreach ( $content as $key => $item ) {
+ $contentText .= $this->makeListItem( $key, $item, $options['list-item'] );
+ }
+ // Compatibility with extensions still using SkinTemplateToolboxEnd or similar
+ if ( is_array( $options['hooks'] ) ) {
+ foreach ( $options['hooks'] as $hook ) {
+ if ( is_string( $hook ) ) {
+ $hookOptions = [];
+ } else {
+ // it should only be an array otherwise
+ $hookOptions = array_values( $hook )[0];
+ $hook = array_keys( $hook )[0];
+ }
+ $contentText .= $this->deprecatedHookHack( $hook, $hookOptions );
+ }
+ }
+
+ $contentText .= Html::closeElement( 'ul' );
+ } else {
+ $contentText = $content;
+ }
+
+ // Special handling for role=search and other weird things
+ $divOptions = [
+ 'role' => 'navigation',
+ 'id' => Sanitizer::escapeIdForAttribute( $options['id'] ),
+ 'title' => Linker::titleAttrib( $options['id'] ),
+ 'aria-labelledby' => $labelId
+ ];
+ if ( !is_array( $options['class'] ) ) {
+ $class = [ $options['class'] ];
+ }
+ if ( !is_array( $options['extra-classes'] ) ) {
+ $extraClasses = [ $options['extra-classes'] ];
+ }
+ $divOptions['class'] = array_merge( $class, $extraClasses );
+
+ $labelOptions = [
+ 'id' => $labelId,
+ 'lang' => $this->get( 'userlang' ),
+ 'dir' => $this->get( 'dir' )
+ ];
+
+ if ( $options['body-wrapper'] !== 'none' ) {
+ $bodyDivOptions = [ 'class' => $options['body-class'] ];
+ if ( is_string( $options['body-id'] ) ) {
+ $bodyDivOptions['id'] = $options['body-id'];
+ }
+ $body = Html::rawElement( $options['body-wrapper'], $bodyDivOptions,
+ $contentText .
+ $this->getAfterPortlet( $name )
+ );
+ } else {
+ $body = $contentText . $this->getAfterPortlet( $name );
+ }
+
+ $html = Html::rawElement( 'div', $divOptions,
+ Html::rawElement( 'h3', $labelOptions, $msgString ) .
+ $body
+ );
+
+ return $html;
+ }
+
+ /**
+ * Wrapper to catch output of old hooks expecting to write directly to page
+ * We no longer do things that way.
+ *
+ * @param string $hook event
+ * @param array $hookOptions args
+ *
+ * @return string html
+ */
+ protected function deprecatedHookHack( $hook, $hookOptions = [] ) {
+ $hookContents = '';
+ ob_start();
+ Hooks::run( $hook, $hookOptions );
+ $hookContents = ob_get_contents();
+ ob_end_clean();
+ if ( !trim( $hookContents ) ) {
+ $hookContents = '';
+ }
+
+ return $hookContents;
+ }
+
+ /**
+ * Better renderer for getFooterIcons and getFooterLinks, based on Vector
+ *
+ * @param array $setOptions Miscellaneous other options
+ * * 'id' for footer id
+ * * 'order' to determine whether icons or links appear first: 'iconsfirst' or links, though in
+ * practice we currently only check if it is or isn't 'iconsfirst'
+ * * 'link-prefix' to set the prefix for all link and block ids; most skins use 'f' or 'footer',
+ * as in id='f-whatever' vs id='footer-whatever'
+ * * 'icon-style' to pass to getFooterIcons: "icononly", "nocopyright"
+ * * 'link-style' to pass to getFooterLinks: "flat" to disable categorisation of links in a
+ * nested array
+ *
+ * @return string html
+ */
+ protected function getFooterBlock( $setOptions = [] ) {
+ // Set options and fill in defaults
+ $options = $setOptions + [
+ 'id' => 'footer',
+ 'order' => 'linksfirst',
+ 'link-prefix' => 'footer',
+ 'icon-style' => 'icononly',
+ 'link-style' => 'flat'
+ ];
+
+ $validFooterIcons = $this->getFooterIcons( $options['icon-style'] );
+ $validFooterLinks = $this->getFooterLinks( $options['link-style'] );
+ $html = '';
+
+ $html .= Html::openElement( 'footer', [
+ 'id' => $options['id'],
+ 'role' => 'contentinfo',
+ 'lang' => $this->get( 'userlang' ),
+ 'dir' => $this->get( 'dir' )
+ ] );
+
+ $iconsHTML = '';
+ if ( count( $validFooterIcons ) > 0 ) {
+ $iconsHTML .= Html::openElement( 'div', [ 'id' => "{$options['link-prefix']}-container-icons"] );
+ $iconsHTML .= Html::openElement( 'div', [ 'id' => "footer-bottom-container"] );
+
+ // Get tagline
+ $iconsHTML .= $this -> getFooterTagline();
+
+ $iconsHTML .= Html::openElement( 'ul', [ 'id' => "{$options['link-prefix']}-icons" ] );
+ foreach ( $validFooterIcons as $blockName => $footerIcons ) {
+ $iconsHTML .= Html::openElement( 'li', [
+ 'id' => Sanitizer::escapeIdForAttribute(
+ "{$options['link-prefix']}-{$blockName}ico"
+ ),
+ 'class' => 'footer-icons'
+ ] );
+ foreach ( $footerIcons as $icon ) {
+ $iconsHTML .= $this->getSkin()->makeFooterIcon( $icon );
+ }
+ $iconsHTML .= Html::closeElement( 'li' );
+ }
+ $iconsHTML .= Html::closeElement( 'ul' );
+ $iconsHTML .= Html::closeElement( 'div' );
+ $iconsHTML .= Html::closeElement( 'div' );
+ }
+
+ $linksHTML = '';
+ if ( count( $validFooterLinks ) > 0 ) {
+ $linksHTML .= Html::openElement( 'div', [ 'id' => "{$options['link-prefix']}-container-list" ] );
+ if ( $options['link-style'] == 'flat' ) {
+ $linksHTML .= Html::openElement( 'ul', [
+ 'id' => "{$options['link-prefix']}-list",
+ 'class' => 'footer-places'
+ ] );
+
+ // Site logo
+ $linksHTML .= Html::rawElement( 'li', [ 'id' => 'sitelogo' ],
+ $this->getLogo()
+ );
+ // Site title
+ $linksHTML .= Html::rawElement( 'li', [ 'id' => 'sitetitle' ],
+ $this->getSiteTitle('text')
+ );
+ // Site description
+ $linksHTML .= Html::rawElement( 'li', [ 'id' => 'sitedesc' ],
+ $this->getFooterDesc()
+ );
+
+ foreach ( $validFooterLinks as $link ) {
+ $linksHTML .= Html::rawElement(
+ 'li',
+ [ 'id' => Sanitizer::escapeIdForAttribute( $link ) ],
+ $this->get( $link )
+ );
+ }
+ $linksHTML .= Html::closeElement( 'ul' );
+ } else {
+ $linksHTML .= Html::openElement( 'div', [ 'id' => "{$options['link-prefix']}-list" ] );
+ foreach ( $validFooterLinks as $category => $links ) {
+ $linksHTML .= Html::openElement( 'ul',
+ [ 'id' => Sanitizer::escapeIdForAttribute(
+ "{$options['link-prefix']}-{$category}"
+ ) ]
+ );
+ foreach ( $links as $link ) {
+ $linksHTML .= Html::rawElement(
+ 'li',
+ [ 'id' => Sanitizer::escapeIdForAttribute(
+ "{$options['link-prefix']}-{$category}-{$link}"
+ ) ],
+ $this->get( $link )
+ );
+ }
+ $linksHTML .= Html::closeElement( 'ul' );
+ }
+ // Site title
+ $linksHTML .= Html::rawElement( 'li', [ 'id' => 'footer-sitetitle' ],
+ $this->getSiteTitle('text')
+ );
+ // Site logo
+ $linksHTML .= Html::rawElement( 'li', [ 'id' => 'footer-sitelogo' ],
+ $this->getLogo()
+ );
+ $linksHTML .= Html::closeElement( 'div' );
+ }
+ $linksHTML .= Html::closeElement( 'div' );
+ }
+
+ if ( $options['order'] == 'iconsfirst' ) {
+ $html .= $iconsHTML . $linksHTML;
+ } else {
+ $html .= $linksHTML . $iconsHTML;
+ }
+
+ $html .= $this->getClear() . Html::closeElement( 'footer' );
+
+ return $html;
+ }
+}
diff --git a/includes/SkinCitizen.php b/includes/SkinCitizen.php
new file mode 100644
index 00000000..e2aaa02f
--- /dev/null
+++ b/includes/SkinCitizen.php
@@ -0,0 +1,52 @@
+addMeta( 'viewport',
+ 'width=device-width, initial-scale=1'
+ );
+ // Edge compatibility
+ $out->addMeta( 'http:X-UA-Compatible',
+ 'IE=edge'
+ );
+
+ $out->addModuleStyles( [
+ 'mediawiki.skinning.content.externallinks',
+ 'skins.citizen',
+ 'skins.citizen.icons',
+ 'skins.citizen.icons.ca',
+ 'skins.citizen.icons.p',
+ 'skins.citizen.icons.toc',
+ 'skins.citizen.icons.es',
+ 'skins.citizen.icons.n',
+ 'skins.citizen.icons.t',
+ 'skins.citizen.icons.pt',
+ 'skins.citizen.icons.footer',
+ 'skins.citizen.icons.badges'
+ ] );
+ $out->addModules( [
+ 'skins.citizen.js'
+ ] );
+ }
+
+ /**
+ * @param $out OutputPage
+ */
+ function setupSkinUserCss( OutputPage $out ) {
+ parent::setupSkinUserCss( $out );
+ }
+}
diff --git a/licenses/Lato OFL.txt b/licenses/Lato OFL.txt
new file mode 100644
index 00000000..98383e3d
--- /dev/null
+++ b/licenses/Lato OFL.txt
@@ -0,0 +1,93 @@
+Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato"
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/licenses/Titillium Web OFL.txt b/licenses/Titillium Web OFL.txt
new file mode 100644
index 00000000..bbaa23a6
--- /dev/null
+++ b/licenses/Titillium Web OFL.txt
@@ -0,0 +1,93 @@
+Copyright (c) 2009-2011 by Accademia di Belle Arti di Urbino and students of MA course of Visual design. Some rights reserved.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/resources/components/bottombar.less b/resources/components/bottombar.less
new file mode 100644
index 00000000..57eb9529
--- /dev/null
+++ b/resources/components/bottombar.less
@@ -0,0 +1,52 @@
+//
+// Citizen - Bottombar Styles
+// https://starcitizen.tools
+//
+
+#mw-bottombar {
+ position: fixed;
+ bottom: 0;
+ display: flex;
+ justify-content: space-between;
+ transform: translate(0px, @header-height + @margin-side);
+ transition: @transition-transform;
+
+ &-contact {
+ display: flex;
+ margin: @margin-side;
+ padding: 0 @margin-side / 2;
+ background-color: #7289da; // Discord color
+ border-radius: 100%;
+ .boxshadow(4);
+ transition: @transition-transform-quick, @transition-box-shadow-quick;
+
+ .citizen-ui-icon {
+ > a {
+ width: @icon-box-size + @icon-padding;
+ height: @header-height;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-indent: -1000px; // Hide text
+
+ &:after {
+ display: block;
+ .resource-loader-menu-icon;
+ transition: @transition-opacity;
+ opacity: 1;
+ }
+
+ &:hover {
+ &:after {
+ opacity: 0.7;
+ }
+ }
+ }
+ }
+ }
+}
+
+// Movable navigation bar
+.nav-up ~ #mw-bottombar {
+ transform: none;
+}
diff --git a/resources/components/common.less b/resources/components/common.less
new file mode 100644
index 00000000..0f651b66
--- /dev/null
+++ b/resources/components/common.less
@@ -0,0 +1,667 @@
+/*
+ * Citizen - Common Styles
+ * https://starcitizen.tools
+*/
+
+@import 'mediawiki.mixins';
+
+/*
+ * Framework
+*/
+
+html {
+ font-size: @html-font-size;
+ scroll-padding-top: @header-height + @margin-side; // fixed header offset, supported by major browsers
+ scroll-behavior: smooth; // not supported by IE, Edge, Safari, and Opera, use JQuery as fallback
+}
+
+html,
+body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ font-family: @fonts;
+ // font-smoothing: antialiased;
+ // -webkit-font-smoothing: antialiased; /* Chrome, Safari */
+ // -moz-osx-font-smoothing: grayscale; /* Firefox */
+
+}
+
+ul {
+ list-style-type: square;
+}
+
+ol {
+ list-style-type: none;
+}
+
+ul, ol {
+ padding: 0;
+}
+
+p, ul, ol {
+ margin: 0.8rem 0 0 0;
+}
+
+pre, code, tt, kbd, samp, .mw-code {
+ font-family: @fonts-monospace;
+ font-size: @content-monospace-size;
+}
+
+pre,
+code,
+.mw-code {
+ color: @base-0;
+ background-color: @base-90;
+ border: 1px solid @base-80;
+}
+
+code {
+ padding: 1px 4px;
+}
+
+pre,
+.mw-code {
+ padding: 1rem;
+ /* Wrap lines in overflow. T2260, T103780 */
+ white-space: pre-wrap;
+}
+
+td {
+ > p,
+ > ul,
+ > ol {
+ &:first-child {
+ margin-top: 0; // Remove margin for the first element in td
+ }
+ }
+}
+
+a {
+ color: @color-link;
+ text-decoration: none!important;
+
+ &:not( [ href ] ) {
+ cursor: pointer; /* Always cursor:pointer even without href */
+ }
+
+ &:hover {
+ color: @color-link-active;
+ }
+
+ &.new {
+ color: @color-link-new!important; // Override visited
+
+ &:hover {
+ color: @color-link-new-active!important;
+ }
+ }
+
+ &:visited {
+ color: @color-link;
+
+ &:hover {
+ color: @color-link-active;
+ }
+ }
+}
+
+.error {
+ padding: 2px 4px;
+ background-color: @red-90;
+ font-size: unset;
+ font-weight: 400;
+}
+
+.firstHeading {
+ padding-top: 1.2rem;
+ line-height: 1.2;
+}
+
+/*
+ * Content
+*/
+
+.mw-body,
+.parsoid-body {
+ margin-top: @header-height + @margin-side;
+ padding: 0 @margin-side;
+ min-height: 60vh; // avoid footer being in the middle of the page
+ direction: ltr;
+}
+
+.mw-wiki-title {
+ font-family: @fonts-secondary!important;
+ text-transform: uppercase;
+
+ a& {
+ color: @base-50;
+ transition: @transition-opacity;
+
+ &:hover,
+ &:active,
+ &:focus {
+ color: @base-30;
+ text-decoration: none;
+ }
+ }
+}
+
+.mw-body {
+ // h1's can exist outside of mw-body-content so some heading styles
+ // need to be defined in mw-body as well
+ & h1,
+ &-content h1,
+ &-content h2,
+ &-content h3,
+ &-content h4 {
+ font-family: @fonts-secondary;
+ }
+
+ & h1,
+ &-content h1 {
+ font-size: @content-h1-size;
+ }
+
+ .firstHeading {
+ margin: 0;
+ font-weight: 600;
+ overflow: inherit; // black magic to wrap in small screen devices
+ }
+
+ #siteSub {
+ display: block;
+ margin: .4rem 0 1.6rem;
+ color: @base-30;
+ font-size: @content-caption-size;
+ }
+
+ .mw-indicators {
+ float: right;
+ line-height: @content-line-height;
+ font-size: @content-body-size;
+ /* Ensure that this is displayed on top of .mw-body-content and clickable */
+ position: relative;
+ z-index: 1;
+ }
+
+ .mw-indicator {
+ display: inline-block;
+ zoom: 1;
+ *display: inline; // stylelint-disable declaration-block-no-duplicate-properties
+ }
+
+ .firstHeading,
+ #siteSub {
+ .content-center;
+ }
+}
+
+.mw-body-content {
+ position: relative;
+ .content-center;
+ line-height: @content-line-height;
+ font-size: @content-body-size;
+ z-index: 0;
+ color: @color-content-text;
+
+ #mw-content-text {
+
+ .mw-parser-output {
+
+ .mw-headline {
+ order: -1; // Make sure that header is the first
+ }
+
+ p {
+ overflow-wrap: break-word;
+ }
+
+ &>h1, &>h2 {
+ margin-top: @content-margin-top * 3;
+ }
+
+ &>h3, &>h4, &>h5, &>h6, &>p+p {
+ margin-top: @content-margin-top * 2;
+ }
+
+ &>h1+h2, &>h2+h3, &>h3+h4, &>h4+h5, &>h5+h6, &>p, &>table {
+ margin-top: @content-margin-top;
+ }
+
+ &>h1, &>h2, &>h3, &>h4, &>h5, &>h6 {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ a:not(.external) {
+ .link-content-text(@color-link-active);
+
+ &.new {
+ background-image: linear-gradient(to right, @color-link-new-active 0, @color-link-new-active 100%);
+ }
+
+ /* Doesn't work for some reason
+ &:visited {
+ background-image: linear-gradient(to right, @color-link-visited 0, @color-link-visited 100%)!important;
+ }
+ */
+ }
+
+ .mw-editsection {
+ position: absolute;
+ display: flex;
+ margin: 0;
+ left: 0;
+ transform: ~'translateX(calc(-100% - @{margin-side}))';
+
+ a {
+ .resource-loader-icon-link-small;
+ padding: @margin-side / 4;
+ opacity: @opacity-icon;
+ transition: @transition-opacity;
+ text-indent: -9999px; // Hide text
+ background: 0!important; // Cancel above styles
+
+ &:before {
+ .resource-loader-icon;
+ background-size: contain;
+ display: block;
+ }
+
+ &:hover,
+ &:active,
+ &:focus {
+ opacity: @opacity-icon-active;
+ }
+ }
+
+ > span {
+ display: none;
+ }
+ }
+
+ .toc {
+ position: fixed;
+ max-width: 450px;
+ height: ~'calc( 100vh - @{header-height} * 2 - @{margin-side} * 4 )';
+ top: @header-height;
+ right: 0;
+ padding: @margin-side * 2 @margin-side;
+ color: #cccccc;
+ overflow: auto;
+
+ &::-webkit-scrollbar {
+ width: 0; // Hide bar on toc
+ }
+
+ @top: linear-gradient(white, rgba(255, 255, 255, 0.001));
+ @bottom: linear-gradient(rgba(255, 255, 255, 0.001), white);
+
+ &:before {
+ top: @header-height;
+ .gradient-overflow(@top);
+ }
+
+ &:after {
+ bottom: @header-height;
+ .gradient-overflow(@bottom);
+ }
+
+ .toctitle {
+ h2 {
+ color: @base-50;
+ font-size: @ui-menu-text-big;
+ }
+ }
+
+ .toctoggle {
+ display: none;
+ }
+
+ .toctext {
+ max-width: 250px;
+ display: flex;
+ }
+
+ a {
+ display: inline-block;
+ margin-top: @content-margin-top;
+ color: @base-50;
+ background: 0;
+
+ &:hover {
+ color: @base-30!important;
+ }
+ }
+
+ &level {
+
+ &-1 {
+ font-size: @content-h2-size * @toc-scaling;
+ }
+
+ &-2 {
+ font-size: @content-h3-size * @toc-scaling;
+ }
+
+ &-3 {
+ font-size: @content-h4-size * @toc-scaling;
+ }
+
+ &-4 {
+ font-size: @content-h5-size * @toc-scaling;
+ }
+
+ &-5 {
+ font-size: @content-h6-size * @toc-scaling;
+ }
+ }
+ .tocnumber {
+ .mixin-screen-reader-text;
+ }
+
+ &.tochidden {
+ > ul {
+ display: block!important; // Force display
+ }
+ }
+
+ > ul {
+ direction: rtl; // Hack to force right alignment, need to fix for RTL
+ margin: @content-margin-top / 2 3px 0 0;
+ position: relative;
+ z-index: 2;
+ border-right: 1px dashed;
+ display: block;
+
+ > li {
+ margin-right: -3px;
+ }
+ }
+
+ ul {
+ list-style-type: disc;
+ list-style-position: inside;
+ }
+
+ ul, .toctitle {
+ text-align: right;
+ }
+ }
+ }
+ }
+
+ p {
+ line-height: inherit;
+ }
+
+ h2 {
+ font-size: @content-h2-size;
+ }
+
+ h3 {
+ font-size: @content-h3-size;
+ }
+
+ h4 {
+ font-size: @content-h4-size;
+ }
+
+ h5 {
+ font-size: @content-h5-size;
+ }
+
+ h6 {
+ font-size: @content-h6-size;
+ }
+
+ table {
+ margin: 0;
+ }
+
+ h1, h2, h3, h4, h5, h6 {
+ margin: 0;
+ padding: 0;
+ color: @color-content-header;
+ font-weight: 400;
+ }
+
+ h5, h6 {
+ font-weight: 600;
+ }
+
+ ul {
+ margin: 0.8rem 0 0 1.6rem;
+ }
+}
+
+// Namespace button
+#p-namespaces {
+ margin: 0 @negative-margin;
+ padding: @margin-side;
+
+ &-label {
+ .mixin-screen-reader-text;
+ }
+
+ ul{
+ margin: 1.6rem 0 0 0;
+ display: flex;
+ justify-content: flex-end; // Right align
+
+ li {
+ list-style: none;
+
+ a {
+ display: flex;
+ align-items: center;
+ padding: 0.4rem 0.8rem;
+ border: 1px solid @base-80;
+ background-color: @base-90;
+ transition: @transition-background-quick, @transition-box-shadow-quick;
+ .boxshadow(1);
+
+ &:hover {
+ background-color: @base-80;
+ .boxshadow(2);
+ }
+
+ span {
+ color: @base-20;
+ }
+
+ &:after {
+ order: -1;
+ content: "";
+ position: relative;
+ width: 14px;
+ height: 14px;
+ margin-right: 8px;
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center;
+ opacity: 0.5;
+ }
+ }
+ }
+ }
+}
+
+.catlinks {
+ margin: 0 @negative-margin;
+ padding: @margin-side;
+ line-height: 1.2;
+
+ .mw-normal-catlinks {
+ font-size: 0; // Hide colon
+
+ > a {
+ display: block;
+ color: @color-content-caption;
+ font-size: @content-caption-size;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ }
+
+ > ul {
+ margin: 0;
+ padding-top: @content-margin-top;
+ display: flex;
+ flex-wrap: wrap;
+ font-size: @content-caption-size; // Reset font
+
+ li {
+ display: block;
+ margin: 0 @margin-side / 2 @margin-side / 2 0;
+ padding: 0;
+ border: 0;
+
+ &:first-of-type {
+ margin-left: 0;
+ }
+
+ a {
+ padding: @margin-side / 4 @margin-side / 2;
+ display: block;
+ border: 1px solid @base-90;
+ background-color: @base-90;
+ color: @color-content-caption!important; // Override other styles
+ .boxshadow(1);
+ transition: @transition-background-quick,
+ @transition-color-quick,
+ @transition-border-quick,
+ @transition-box-shadow-quick;
+
+ &:hover {
+ background-color: @color-link-active;
+ border-color: @color-link-active;
+ color:@base-100!important;
+ .boxshadow(2);
+ }
+ &.new:hover {
+ background-color: @color-link-new-active;
+ border-color: @color-link-new-active;
+ }
+ }
+ }
+ }
+ }
+}
+
+// TODO: Flexible value
+@screen1: @page-width + @margin-side * 2;
+@screen2: @footer-width;
+@screen3: @footer-width + @margin-side * 8;
+
+@media only screen and (max-width: @screen1) {
+ .catlinks,
+ #p-namespaces {
+ margin: 0!important; // somehow got overrided
+ padding: @margin-side 0;
+ }
+}
+
+@media only screen and (max-width: @screen2) {
+ .mw-editsection {
+ position: relative!important;
+ transform: none!important;
+ }
+
+ .catlinks,
+ #p-namespaces {
+ margin: 0 ~"calc((100vw - @{page-width}) / -2)";
+ }
+}
+
+@media only screen and (max-width: @screen3) {
+ .webfonts-changed .toc {
+ display: block!important; // Hide until everything is loaded
+ }
+
+ .mw-body-content #mw-content-text .mw-parser-output .toc {
+
+ display: none;
+ background: @base-100;
+ z-index: 1;
+ height: 100vh;
+ margin-top: -@header-height;
+ padding: 0;
+ .boxshadow(3);
+
+ &:before,
+ &:after {
+ content: unset; // Disable fade
+ }
+
+ .toctoggle {
+ display: block;
+ font-size: 0;
+
+ a {
+ position: fixed;
+ z-index: 7;
+ right: 0;
+ bottom: 0;
+ margin: @margin-side;
+ padding: 0 @margin-side / 2;
+ width: 39px;
+ height: 56px;
+ display: block;
+ background-color: @base-90;
+ border-radius: 100%;
+ .boxshadow(4);
+ transform: translate(0px, @header-height + @margin-side);
+ transition: @transition-opacity, @transition-transform, @transition-box-shadow-quick;
+
+ &:hover {
+ .boxshadow(5);
+
+ &:before {
+ opacity: @opacity-icon-active;
+ }
+ }
+
+ &:before {
+ .resource-loader-icon;
+ display: block;
+ opacity: @opacity-icon;
+ }
+ }
+ }
+
+ .toctitle h2 {
+ display: none;
+ }
+
+ &.tochidden {
+ > ul {
+ display: none!important; // Reset hide
+ transform: translateX(300px + @margin-side);
+ }
+ }
+
+ > ul {
+ margin: 0 @margin-side;
+ padding: @header-height + @margin-side 0 @header-height + @margin-side * 2; // More scroll spaces
+ transform: none;
+ transition: @transition-transform;
+ }
+ }
+
+ .nav-up {
+ ~ .mw-body .mw-body-content #mw-content-text .mw-parser-output .toc .toctoggle a {
+ transform: none;
+ }
+ }
+}
+
+/*
+// Logged in styles
+.not-logged {
+ .mw-editsection {
+ display: none!important; // Hide edit icons when not logged in
+ }
+}
+*/
diff --git a/resources/components/footer.less b/resources/components/footer.less
new file mode 100644
index 00000000..3bd5a0b1
--- /dev/null
+++ b/resources/components/footer.less
@@ -0,0 +1,219 @@
+//
+// Citizen - Footer Styles
+// https://starcitizen.tools
+//
+
+
+#footer {
+ z-index: 9; // High enough so it covers the floating UI
+ overflow: hidden; //stop background hacks from destroying viewbox
+ position: relative;
+ direction: ltr;
+ margin-top: @margin-side * 2;
+ color: @color-footer-text;
+ background: @color-footer-background-50;
+ font-size: @ui-menu-text;
+ line-height: @footer-line-height;
+
+ a {
+ color: @color-footer-link;
+
+ &:hover,
+ &:active,
+ &:focus {
+ color: @color-footer-link-active;
+ text-decoration: none;
+ }
+ }
+
+ ul, li {
+ margin: 0;
+ }
+
+ li {
+ list-style: none;
+ }
+
+ div {
+ display: flex;
+ }
+
+ // Bless Flexbox for saving me from PHP nightmare
+ // OPTIMIZE: Implement nightmare
+ &-list {
+ margin: 0 auto!important;
+ max-width: @footer-width;
+ display: flex;
+ flex-wrap: wrap;
+
+ > li {
+ position: relative;
+ padding-left: @margin-side;
+ padding-right: @margin-side;
+ }
+
+ #lastmod {
+ order: 1;
+ padding-top: 12px;
+ padding-bottom: 12px;
+ display: flex;
+
+ a {
+ margin-right: 5px;
+ }
+
+ // Change icon
+ &:before {
+ margin-right: @icon-padding;
+ .resource-loader-list-icon;
+ opacity: @opacity-icon;
+ }
+
+ // Background
+ &:after {
+ content: "";
+ background-color: @color-footer-background-60;
+ height: 100%;
+ width: 600vw; // so long so it is always there
+ top: 0;
+ margin: 0 -100vw; // so long so you can't see the end
+ display: block;
+ z-index: -1;
+ position: absolute;
+ overflow: hidden; // now you literally can't see the end
+ }
+ }
+
+ #sitelogo {
+ order: 2;
+ }
+
+ #sitetitle {
+ order: 3;
+ width: 100%;
+ margin-top: @margin-side * 3;
+ margin-bottom: @margin-side / 2;
+
+ .mw-wiki-title {
+ color: @base-90;
+ font-size: 20px;
+ letter-spacing: 2px;
+ }
+ }
+
+ #sitedesc {
+ order: 4;
+ margin-top: @margin-side / 2;
+ }
+
+ #about {
+ order: 10;
+ }
+
+ #privacy {
+ order: 11;
+ }
+
+ #disclaimer {
+ order: 12;
+ }
+
+ #about,
+ #privacy,
+ #disclaimer {
+ margin: 0 @margin-side / 2;
+ padding: 0;
+
+ a {
+ padding: 10px;
+ display: block;
+ }
+ }
+
+ #sitedesc,
+ #copyright {
+ margin-bottom: @margin-side / 2;
+ width: 100%;
+ }
+
+ #copyright{
+ margin-top: @margin-side;
+ order: 20;
+ opacity: 0.5;
+ font-size: 12px;
+ }
+ }
+
+ &-container-icons {
+ margin-top: @margin-side * 2;
+ background-color: @color-footer-background-40;
+
+ #footer-bottom-container {
+ margin: 0 auto;
+ padding: @margin-side / 2 0;
+ width: 100%;
+ max-width: @footer-width;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: space-between;
+
+ #mw-footer-tagline {
+ padding: 10px 20px;
+ }
+
+ #footer-icons {
+ padding: 0 10px;
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+
+ a {
+ padding: 15px 10px;
+ height: 50px;
+ display: block;
+ opacity: 0.5;
+ transition: @transition-opacity;
+
+ &:before {
+ content:"";
+ .resource-loader-icon;
+ display: block;
+ background-size: contain;
+ }
+
+ &:hover {
+ opacity: 0.7;
+ }
+
+
+ img {
+ display: none; // hide placeholder images
+ }
+ }
+ }
+ }
+ }
+
+ // Icon styles
+ &-madebyico a {
+ width: 50px;
+ }
+
+ &-gdprcomplianceico a {
+ width: 50px;
+ }
+
+ &-monitoredbyico a {
+ width: 95px;
+ margin-bottom: -3px; //dirty fix
+ }
+
+ &-poweredbyico a {
+ width: 95px;
+ }
+
+ &-copyrightico a {
+ width: 110px;
+ }
+}
diff --git a/resources/components/navigation.less b/resources/components/navigation.less
new file mode 100644
index 00000000..687be56b
--- /dev/null
+++ b/resources/components/navigation.less
@@ -0,0 +1,347 @@
+/*
+ * Citizen - Navigation Styles
+ * https://starcitizen.tools
+*/
+
+/*
+ * Header
+*/
+
+.mw-header-container {
+ position: fixed;
+ z-index: 10;
+ top: 0;
+ width: 100vw;
+ height: @header-height;
+ display: flex;
+ justify-content: space-between;
+ font-family: @fonts-secondary;
+ transition: @transition-transform;
+
+ ul {
+ list-style: none;
+ }
+
+ ul, li {
+ margin: 0;
+ display: block;
+ }
+
+ // Hide header when scroll
+ &.nav-up {
+ transform: translate(0px, -@header-height);
+ }
+
+ .mw-header-icons {
+ display: inherit;
+ }
+
+ .mw-header-end {
+ width: @icon-box-size + @margin-side + @icon-padding;
+ height: @header-height;
+ user-select: none;
+
+ // Input button hack
+ > input {
+ width: inherit;
+ height: inherit;
+ position: absolute;
+ z-index: 8;
+ margin: 0;
+ display: block;
+ opacity: 0;
+ cursor: pointer;
+ -webkit-touch-callout: none;
+ }
+ }
+}
+/*
+ * Hamburger menu
+ * Modified from https://codepen.io/erikterwan/pen/EVzeRP and https://codepen.io/oxla/pen/zgvqmM
+ */
+#mw-header-menu {
+
+ .mw-wiki-title {
+ letter-spacing: 2px;
+ }
+
+ input {
+ &:checked {
+
+ // Show drawer
+ ~ #mw-header-menu-drawer {
+ transform: none;
+ will-change: none;
+ }
+
+ // Transform all the slices of hamburger into a crossmark
+ ~ #mw-header-menu-toggle > span {
+ opacity: @opacity-icon;
+ transform: rotate(45deg) translate(-5px, -7px);
+
+ // Reflect line
+ &:nth-last-child(2) {
+ transform: rotate(-45deg) translate(-2px, 6px);
+ }
+
+ // Hide line
+ &:nth-last-child(3) {
+ opacity: 0;
+ transform: rotate(0deg) scale(0.2, 0.2);
+ }
+ }
+ }
+ // Hover state
+ &:hover ~ #mw-header-menu-toggle > span {
+ opacity: @opacity-icon-active;
+
+ &:first-child {
+ transform: translate(-50%,0);
+ }
+ }
+ }
+
+ &-toggle {
+ width: @icon-size;
+ height: @icon-size;
+ margin: (@header-height - @icon-size) / 2 @icon-padding (@header-height - @icon-size) / 2 (@icon-margin * 2 + @margin-side * 2) / 2;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+
+ span {
+ z-index: 5;
+ margin: 1.5px 0;
+ width: @icon-size;
+ height: 2px;
+ display: block;
+ background: @base-0;
+ opacity: @opacity-icon;
+ transform-origin: 4px 0px;
+ transition: @transition-transform,
+ @transition-background-quick,
+ @transition-opacity;
+
+ &:first-child {
+ transform-origin: 0% 0%;
+ }
+ &:nth-last-child(2) {
+ transform-origin: 0% 100%;
+ }
+ }
+ }
+
+ &-drawer {
+ position: absolute;
+ z-index: 4;
+ top: 0;
+ padding-top: @header-height;
+ width: @drawer-width;
+ max-width: 100vw; // In case if someone has super small screen
+ height: 100vh;
+ .boxshadow(3);
+ background: @menu-background;
+ transform-origin: 0% 0%;
+ transform: translate(-110%, 0); // Shadow bleeding with 100%
+ transition: @transition-transform;
+ will-change: transform; // Help with performance
+
+ &-container {
+ height: ~"calc(100vh - @{header-height})";
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ transition: @transition-height;
+
+ .mw-wiki-title {
+ padding: @margin-side;
+ }
+
+ #p-nt-container {
+ overflow: auto;
+ flex-grow: 1;
+ }
+
+ #p-navigation-label {
+ .mixin-screen-reader-text;
+ }
+
+ #mw-user-links {
+
+ #p-personal {
+ margin-top: @margin-side / 2;
+ padding-top: @margin-side / 2;
+ border-top: 1px solid @base-80;
+
+ h3 {
+ .mixin-screen-reader-text;
+ }
+
+ #pt-userpage {
+ margin-bottom: @margin-side / 2;
+
+ a {
+ justify-content: unset;
+
+ &:after {
+ margin: 0;
+ width: @icon-box-size;
+ height: @icon-box-size;
+ }
+ }
+
+ span {
+ order: 2;
+ padding-left: @margin-side;
+ }
+ }
+
+ #pt-login {
+ a {
+ .button-blue;
+
+ &:hover,
+ &:active,
+ &:focus {
+ .button-blue-active;
+ }
+ }
+ }
+
+ #pt-logout {
+ a {
+ .button-red;
+
+ &:hover,
+ &:active,
+ &:focus {
+ .button-red-active;
+ }
+ }
+ }
+ }
+ }
+
+ h3 {
+ margin: @margin-side / 2 0 0 0;
+ padding: @margin-side / 2 @margin-side;
+ color: @color-item-header;
+ font-size: @ui-menu-header;
+ font-weight: normal;
+ letter-spacing: 1px;
+ }
+
+ span {
+ display: block;
+ }
+
+ a {
+ .menu-item-link;
+ align-items: center;
+ justify-content: space-between;
+ padding: @padding-menu-item-big;
+ font-size: @ui-menu-text-big;
+ font-family: @fonts;
+
+ &:after {
+ .resource-loader-list-icon;
+ margin-left: @icon-padding;
+ opacity: 0.4;
+ display: block;
+ background-size: contain;
+ transition: @transition-opacity-quick;
+ }
+
+ &:hover {
+ .menu-item-link-hover;
+
+ &:after {
+ opacity: 0.6;
+ }
+ }
+
+ &:active {
+ .menu-item-link-active;
+ }
+
+ &:focus {
+ .menu-item-link-focus;
+ }
+ }
+ }
+ }
+}
+
+// Sidebar site title
+#mw-sidebar-sitename {
+ position: fixed;
+ visibility: visible;
+ top: @sidebar-sitename-height + @header-height + @margin-side;
+ left: @margin-side;
+ font-size: 11px;
+ letter-spacing: 4px;
+ transform: translateY(0) rotate(-90deg);
+ transform-origin: top left;
+ transition: @transition-transform, @transition-opacity;
+}
+
+// Nav up stuff
+.nav-up {
+ ~ #mw-sidebar-sitename {
+ transform: translateY(-54px) rotate(-90deg);
+ }
+
+ #mw-header-menu-drawer-container {
+ height: 100vh;
+ }
+}
+
+// Bypass calculation
+@mw-sidebar-sitename-max-width: @margin-side * 5 + @page-width;
+
+@media only screen and (max-width: @mw-sidebar-sitename-max-width) {
+ #mw-sidebar-sitename {
+ z-index: -1; // remove link
+ opacity: 0; // hide visual
+ }
+}
+
+/*
+ * User icon bar
+ */
+#p-personal-extra {
+ ul {
+ height: 56px;
+ display: flex;
+ align-items: center;
+ }
+}
+
+/*
+ * Hide stuff
+ */
+#feedlinks,
+#pt-anontalk,
+#pt-anoncontribs {
+ display: none;
+}
+
+// RTL tweaks
+.rtl {
+ #mw-sidebar-sitename {
+ left: unset;
+ right: @margin-side;
+ transform: translateY(0) rotate(90deg);
+ transform-origin: top right;
+ }
+
+ .nav-up ~ #mw-sidebar-sitename {
+ transform: translateY(-54px) rotate(90deg);
+ }
+
+ #mw-header-menu-drawer {
+ transform: translate(100%, 0);
+ }
+}
diff --git a/resources/components/page-tools.less b/resources/components/page-tools.less
new file mode 100644
index 00000000..cdd6f75a
--- /dev/null
+++ b/resources/components/page-tools.less
@@ -0,0 +1,151 @@
+//
+// Citizen - Page Tools Styles
+// https://starcitizen.tools
+// TODO: RTL styles and more flexible
+//
+
+// Hide selected item
+.mw-portlet li.selected {
+ .mixin-screen-reader-text;
+}
+
+#page-tools {
+ z-index: 5;
+ position: absolute;
+ margin-top: 1.3rem;
+ display: flex;
+ transform: translateX(~"calc( (100vw - @{page-width}) / 2 - @{margin-side} * 2 - 100%)"); // magic
+
+ h3 {
+ .mixin-screen-reader-text;
+ }
+
+ ul {
+ margin: 0;
+ display: flex;
+ list-style: none;
+ }
+
+ li {
+ margin: 0;
+ }
+
+ #p-views {
+ li > a {
+ .resource-loader-icon-link;
+ padding: 5px;
+ opacity: @opacity-icon;
+ transition: @transition-opacity;
+
+ &:hover {
+ opacity: @opacity-icon-active;
+ }
+
+ &:after {
+ .resource-loader-icon;
+ }
+
+ span {
+ .mixin-screen-reader-text;
+ }
+ }
+ }
+
+ #p-actions {
+ > nav {
+ .resource-loader-icon-link;
+ padding: 5px;
+ cursor: pointer;
+ // transition: @transition-opacity-quick; - Hidden behind the menu anyways
+
+ // TODO: Need to make value more flexible
+ ul {
+ z-index: -1;
+ pointer-events: none;
+ .menu-container;
+ position: absolute;
+ opacity: 0;
+ .boxshadow(4);
+ transition: @transition-opacity-quick, @transition-box-shadow-quick;
+
+ a {
+ .menu-item-link;
+ justify-content: space-between;
+ font-size: @ui-menu-text;
+ padding: @padding-menu-item;
+
+ &:after {
+ .resource-loader-list-icon;
+ margin-left: @icon-padding;
+ opacity: @opacity-icon;
+ }
+
+ &:hover,
+ &:active,
+ &:focus {
+ &:after {
+ opacity: @opacity-icon-active;
+ }
+ }
+
+ &:hover {
+ .menu-item-link-hover;
+ }
+
+ &:active {
+ .menu-item-link-active;
+ }
+
+ &:focus {
+ .menu-item-link-focus;
+ }
+ }
+ }
+
+ &:before {
+ .resource-loader-menu-icon;
+ opacity: @opacity-icon;
+ }
+
+/*
+ * Hidden behind the menu anyways
+ * &:hover:after {
+ * opacity: @opacity-icon-active;
+ * }
+*/
+
+ &:hover ul {
+ z-index: 5;
+ opacity: 1;
+ pointer-events: auto;
+ }
+ }
+ }
+}
+
+// TODO: Flexible value
+@media only screen and (max-width: 1250px) {
+ #page-tools {
+ position: relative;
+ margin-left: @margin-side / 2;
+ float: right;
+ transform: none;
+
+ #p-actions > nav ul {
+ right: 0;
+ }
+ }
+}
+
+@media only screen and (max-width: 500px) {
+ #page-tools {
+ position: relative;
+ margin-left: 0;
+ transform: none;
+ float: none;
+
+ #p-actions > nav ul {
+ right: unset;
+ }
+ }
+}
diff --git a/resources/components/search.less b/resources/components/search.less
new file mode 100644
index 00000000..d252483b
--- /dev/null
+++ b/resources/components/search.less
@@ -0,0 +1,166 @@
+//
+// Citizen - Search Styles
+// https://starcitizen.tools
+//
+
+// TODO: Make it configurable and flexible
+#site-search {
+
+ #p-search {
+ position: absolute;
+ z-index: -1; // Not interactable
+ margin: 7px 0 8px; // 1px h3 screen reader
+ top: 0;
+ right: @icon-box-size + @icon-padding * 2;
+ opacity: 0;
+ .boxshadow(4);
+ transition: @transition-opacity;
+
+ // Hide title
+ h3 {
+ .mixin-screen-reader-text;
+ }
+
+ // Search field
+ #searchInput {
+ padding: 12px 15px;
+ width: 0;
+ max-width: calc(~"100vw -" @icon-box-size * 2 + @icon-padding * 4 + @margin-side );
+ border: 1px solid @base-90;
+ transition: @transition-width, @transition-box-shadow;
+
+ &:focus {
+ outline: 0;
+ .boxshadow(5);
+ }
+ }
+
+ // Search field button
+ #searchGoButton {
+ .button-blue;
+ width: @icon-box-size + @icon-padding * 2;
+ height: 41px;
+ border: 0;
+ cursor: pointer; //somehow it is not pointer
+
+ &:hover,
+ &:active,
+ &:focus {
+ .button-blue-active;
+ }
+ }
+ }
+
+ #search-icon-container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: inherit;
+
+ #search-icon {
+ opacity: @opacity-icon;
+ position: absolute;
+ margin-top: -2px;
+ margin-left: -2px;
+ width: 10px;
+ height: 10px;
+ border: solid 2px @base-0;
+ border-radius: 100%;
+ transform: rotate(-45deg);
+ transition: @transition-transform,
+ @transition-height,
+ @transition-opacity,
+ @transition-border-color;
+
+ &:before,
+ &:after {
+ content: '';
+ position: absolute;
+ width: 2px;
+ background-color: @base-0;
+ transition: inherit;
+ }
+
+ &:before{
+ top: 10px;
+ left: 3px;
+ height: 10px;
+ }
+
+ &:after {
+ opacity: 0;
+ top: -1px;
+ left: 4px;
+ height: 18px;
+ transform: rotate(-90deg);
+ }
+ }
+ }
+
+ > input {
+ &:checked {
+ ~ #p-search {
+ z-index: 5;
+ opacity: 1;
+
+ #searchInput {
+ width: 400px;
+ }
+ }
+
+ ~ #search-icon-container #search-icon {
+ border-color: transparent;
+
+ &:before {
+ height: 18px;
+ transform: translate(1px, -11px);
+ }
+
+ &:after {
+ opacity: 1;
+ }
+
+ &:hover {
+ ~ #search-icon-container #search-icon {
+ border-color: @base-0;
+
+ &:after {
+ opacity: 0;
+ }
+ }
+ }
+ }
+
+ &:hover {
+ ~ #search-icon-container #search-icon {
+ border-color: @base-0;
+
+ &:after {
+ height: 12px;
+ }
+ }
+ }
+ }
+
+ &:hover {
+ ~ #search-icon-container #search-icon {
+ opacity: @opacity-icon-active;
+
+ &:before {
+ height: 5px;
+ transform: translate(0px, 5px);
+ }
+ }
+ }
+ }
+}
+
+// RTL tweaks
+.rtl {
+ #site-search {
+ #p-search {
+ left: @icon-box-size + @margin-side + @icon-padding;
+ right: unset;
+ }
+ }
+}
diff --git a/resources/components/wikitable.less b/resources/components/wikitable.less
new file mode 100644
index 00000000..b16a8c0c
--- /dev/null
+++ b/resources/components/wikitable.less
@@ -0,0 +1,42 @@
+//
+// Citizen - Wikitable Styles
+// https://starcitizen.tools
+//
+
+table.wikitable {
+ margin-top: @content-margin-top * 2!important;
+ background-color: transparent;
+ color: @base-20;
+ border: 0;
+
+ // TEMP solution to fix overflow
+ display: block;
+ max-width: 100vw;
+ overflow: auto;
+
+ tr {
+ vertical-align: top;
+
+ th {
+ padding: @margin-side / 2 @margin-side @margin-side 0;
+ border: 0;
+ border-top: 1px solid currentColor;
+ color: @base-30;
+ background-color: transparent;
+ font-size: @content-caption-size;
+ letter-spacing: 1px;
+ text-transform: uppercase;
+ text-align: left;
+ }
+
+ td {
+ border: 0;
+ border-bottom: 1px solid @base-80;
+ padding: @margin-side / 2 @margin-side @margin-side / 2 0;
+ }
+
+ &:hover {
+ background-color: @base-90;
+ }
+ }
+}
diff --git a/resources/font-face.less b/resources/font-face.less
new file mode 100644
index 00000000..bad610c3
--- /dev/null
+++ b/resources/font-face.less
@@ -0,0 +1,82 @@
+// Font-face stylesheet
+
+// Directory of hosted font
+@font-directory: 'fonts/';
+
+@font-face {
+ font-family: 'Titillium Web';
+ src: url('@{font-directory}TitilliumWeb-Regular.eot');
+ src: local('Titillium Web Regular'), local('TitilliumWeb-Regular'),
+ url('@{font-directory}TitilliumWeb-Regular.eot?#iefix') format('embedded-opentype'),
+ url('@{font-directory}TitilliumWeb-Regular.woff2') format('woff2'),
+ url('@{font-directory}TitilliumWeb-Regular.woff') format('woff'),
+ url('@{font-directory}TitilliumWeb-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Roboto';
+ src: url('@{font-directory}Roboto-Italic.eot');
+ src: local('Roboto Italic'), local('Roboto-Italic'),
+ url('@{font-directory}Roboto-Italic.eot?#iefix') format('embedded-opentype'),
+ url('@{font-directory}Roboto-Italic.woff2') format('woff2'),
+ url('@{font-directory}Roboto-Italic.woff') format('woff'),
+ url('@{font-directory}Roboto-Italic.ttf') format('truetype');
+ font-weight: normal;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Titillium Web';
+ src: url('@{font-directory}TitilliumWeb-Bold.eot');
+ src: local('Titillium Web Bold'), local('TitilliumWeb-Bold'),
+ url('@{font-directory}TitilliumWeb-Bold.eot?#iefix') format('embedded-opentype'),
+ url('@{font-directory}TitilliumWeb-Bold.woff2') format('woff2'),
+ url('@{font-directory}TitilliumWeb-Bold.woff') format('woff'),
+ url('@{font-directory}TitilliumWeb-Bold.ttf') format('truetype');
+ font-weight: bold;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Titillium Web';
+ src: url('@{font-directory}TitilliumWeb-Italic.eot');
+ src: local('Titillium Web Italic'), local('TitilliumWeb-Italic'),
+ url('@{font-directory}TitilliumWeb-Italic.eot?#iefix') format('embedded-opentype'),
+ url('@{font-directory}TitilliumWeb-Italic.woff2') format('woff2'),
+ url('@{font-directory}TitilliumWeb-Italic.woff') format('woff'),
+ url('@{font-directory}TitilliumWeb-Italic.ttf') format('truetype');
+ font-weight: normal;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Roboto';
+ src: url('@{font-directory}Roboto-Bold.eot');
+ src: local('Roboto Bold'), local('Roboto-Bold'),
+ url('@{font-directory}Roboto-Bold.eot?#iefix') format('embedded-opentype'),
+ url('@{font-directory}Roboto-Bold.woff2') format('woff2'),
+ url('@{font-directory}Roboto-Bold.woff') format('woff'),
+ url('@{font-directory}Roboto-Bold.ttf') format('truetype');
+ font-weight: bold;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Roboto';
+ src: url('@{font-directory}Roboto-Regular.eot');
+ src: local('Roboto'), local('Roboto-Regular'),
+ url('@{font-directory}Roboto-Regular.eot?#iefix') format('embedded-opentype'),
+ url('@{font-directory}Roboto-Regular.woff2') format('woff2'),
+ url('@{font-directory}Roboto-Regular.woff') format('woff'),
+ url('@{font-directory}Roboto-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+ font-display: swap;
+}
diff --git a/resources/fonts/Roboto-Bold.eot b/resources/fonts/Roboto-Bold.eot
new file mode 100644
index 00000000..e928a0d9
Binary files /dev/null and b/resources/fonts/Roboto-Bold.eot differ
diff --git a/resources/fonts/Roboto-Bold.ttf b/resources/fonts/Roboto-Bold.ttf
new file mode 100644
index 00000000..e612852d
Binary files /dev/null and b/resources/fonts/Roboto-Bold.ttf differ
diff --git a/resources/fonts/Roboto-Bold.woff b/resources/fonts/Roboto-Bold.woff
new file mode 100644
index 00000000..4536546c
Binary files /dev/null and b/resources/fonts/Roboto-Bold.woff differ
diff --git a/resources/fonts/Roboto-Bold.woff2 b/resources/fonts/Roboto-Bold.woff2
new file mode 100644
index 00000000..4c8d525f
Binary files /dev/null and b/resources/fonts/Roboto-Bold.woff2 differ
diff --git a/resources/fonts/Roboto-Italic.eot b/resources/fonts/Roboto-Italic.eot
new file mode 100644
index 00000000..b7b7b0e8
Binary files /dev/null and b/resources/fonts/Roboto-Italic.eot differ
diff --git a/resources/fonts/Roboto-Italic.ttf b/resources/fonts/Roboto-Italic.ttf
new file mode 100644
index 00000000..b85c4356
Binary files /dev/null and b/resources/fonts/Roboto-Italic.ttf differ
diff --git a/resources/fonts/Roboto-Italic.woff b/resources/fonts/Roboto-Italic.woff
new file mode 100644
index 00000000..d15b4b3b
Binary files /dev/null and b/resources/fonts/Roboto-Italic.woff differ
diff --git a/resources/fonts/Roboto-Italic.woff2 b/resources/fonts/Roboto-Italic.woff2
new file mode 100644
index 00000000..ca6d6c4f
Binary files /dev/null and b/resources/fonts/Roboto-Italic.woff2 differ
diff --git a/resources/fonts/Roboto-Regular.eot b/resources/fonts/Roboto-Regular.eot
new file mode 100644
index 00000000..a88ce3f7
Binary files /dev/null and b/resources/fonts/Roboto-Regular.eot differ
diff --git a/resources/fonts/Roboto-Regular.ttf b/resources/fonts/Roboto-Regular.ttf
new file mode 100644
index 00000000..cb8ffcf1
Binary files /dev/null and b/resources/fonts/Roboto-Regular.ttf differ
diff --git a/resources/fonts/Roboto-Regular.woff b/resources/fonts/Roboto-Regular.woff
new file mode 100644
index 00000000..5421d5dd
Binary files /dev/null and b/resources/fonts/Roboto-Regular.woff differ
diff --git a/resources/fonts/Roboto-Regular.woff2 b/resources/fonts/Roboto-Regular.woff2
new file mode 100644
index 00000000..b6487a6f
Binary files /dev/null and b/resources/fonts/Roboto-Regular.woff2 differ
diff --git a/resources/fonts/Roboto-RegularItalic.ttf b/resources/fonts/Roboto-RegularItalic.ttf
new file mode 100644
index 00000000..5fd05c3b
Binary files /dev/null and b/resources/fonts/Roboto-RegularItalic.ttf differ
diff --git a/resources/fonts/TitilliumWeb-Bold.eot b/resources/fonts/TitilliumWeb-Bold.eot
new file mode 100644
index 00000000..040bd7b0
Binary files /dev/null and b/resources/fonts/TitilliumWeb-Bold.eot differ
diff --git a/resources/fonts/TitilliumWeb-Bold.ttf b/resources/fonts/TitilliumWeb-Bold.ttf
new file mode 100644
index 00000000..b51a4d63
Binary files /dev/null and b/resources/fonts/TitilliumWeb-Bold.ttf differ
diff --git a/resources/fonts/TitilliumWeb-Bold.woff b/resources/fonts/TitilliumWeb-Bold.woff
new file mode 100644
index 00000000..e0d7f131
Binary files /dev/null and b/resources/fonts/TitilliumWeb-Bold.woff differ
diff --git a/resources/fonts/TitilliumWeb-Bold.woff2 b/resources/fonts/TitilliumWeb-Bold.woff2
new file mode 100644
index 00000000..fc9283a5
Binary files /dev/null and b/resources/fonts/TitilliumWeb-Bold.woff2 differ
diff --git a/resources/fonts/TitilliumWeb-Italic.eot b/resources/fonts/TitilliumWeb-Italic.eot
new file mode 100644
index 00000000..c930406f
Binary files /dev/null and b/resources/fonts/TitilliumWeb-Italic.eot differ
diff --git a/resources/fonts/TitilliumWeb-Italic.ttf b/resources/fonts/TitilliumWeb-Italic.ttf
new file mode 100644
index 00000000..3f8d1be3
Binary files /dev/null and b/resources/fonts/TitilliumWeb-Italic.ttf differ
diff --git a/resources/fonts/TitilliumWeb-Italic.woff b/resources/fonts/TitilliumWeb-Italic.woff
new file mode 100644
index 00000000..f80105ae
Binary files /dev/null and b/resources/fonts/TitilliumWeb-Italic.woff differ
diff --git a/resources/fonts/TitilliumWeb-Italic.woff2 b/resources/fonts/TitilliumWeb-Italic.woff2
new file mode 100644
index 00000000..8902a5d1
Binary files /dev/null and b/resources/fonts/TitilliumWeb-Italic.woff2 differ
diff --git a/resources/fonts/TitilliumWeb-Regular.eot b/resources/fonts/TitilliumWeb-Regular.eot
new file mode 100644
index 00000000..1c9db214
Binary files /dev/null and b/resources/fonts/TitilliumWeb-Regular.eot differ
diff --git a/resources/fonts/TitilliumWeb-Regular.ttf b/resources/fonts/TitilliumWeb-Regular.ttf
new file mode 100644
index 00000000..a54ad4b9
Binary files /dev/null and b/resources/fonts/TitilliumWeb-Regular.ttf differ
diff --git a/resources/fonts/TitilliumWeb-Regular.woff b/resources/fonts/TitilliumWeb-Regular.woff
new file mode 100644
index 00000000..90cf46f2
Binary files /dev/null and b/resources/fonts/TitilliumWeb-Regular.woff differ
diff --git a/resources/fonts/TitilliumWeb-Regular.woff2 b/resources/fonts/TitilliumWeb-Regular.woff2
new file mode 100644
index 00000000..e4ed8971
Binary files /dev/null and b/resources/fonts/TitilliumWeb-Regular.woff2 differ
diff --git a/resources/fonts/TitilliumWeb-RegularItalic.ttf b/resources/fonts/TitilliumWeb-RegularItalic.ttf
new file mode 100644
index 00000000..266d5cb8
Binary files /dev/null and b/resources/fonts/TitilliumWeb-RegularItalic.ttf differ
diff --git a/resources/images/badges/CCBYSA4.svg b/resources/images/badges/CCBYSA4.svg
new file mode 100644
index 00000000..7a46ae73
--- /dev/null
+++ b/resources/images/badges/CCBYSA4.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/badges/GDPR.svg b/resources/images/badges/GDPR.svg
new file mode 100644
index 00000000..9030d8c2
--- /dev/null
+++ b/resources/images/badges/GDPR.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/badges/MadeByTheCommunity.svg b/resources/images/badges/MadeByTheCommunity.svg
new file mode 100644
index 00000000..97694563
--- /dev/null
+++ b/resources/images/badges/MadeByTheCommunity.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/badges/MonitoredByWikiApiary.svg b/resources/images/badges/MonitoredByWikiApiary.svg
new file mode 100644
index 00000000..48c1cc31
--- /dev/null
+++ b/resources/images/badges/MonitoredByWikiApiary.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/badges/PoweredByMediawiki.svg b/resources/images/badges/PoweredByMediawiki.svg
new file mode 100644
index 00000000..ec6f8115
--- /dev/null
+++ b/resources/images/badges/PoweredByMediawiki.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/icons/ToC.svg b/resources/images/icons/ToC.svg
new file mode 100644
index 00000000..b71332eb
--- /dev/null
+++ b/resources/images/icons/ToC.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/resources/images/icons/article.svg b/resources/images/icons/article.svg
new file mode 100644
index 00000000..820abdd8
--- /dev/null
+++ b/resources/images/icons/article.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/resources/images/icons/beta.svg b/resources/images/icons/beta.svg
new file mode 100644
index 00000000..7a37c5a7
--- /dev/null
+++ b/resources/images/icons/beta.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/resources/images/icons/contributions.svg b/resources/images/icons/contributions.svg
new file mode 100644
index 00000000..1af18f8f
--- /dev/null
+++ b/resources/images/icons/contributions.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/icons/delete.svg b/resources/images/icons/delete.svg
new file mode 100644
index 00000000..1751526a
--- /dev/null
+++ b/resources/images/icons/delete.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/resources/images/icons/discord.svg b/resources/images/icons/discord.svg
new file mode 100644
index 00000000..15ee8f92
--- /dev/null
+++ b/resources/images/icons/discord.svg
@@ -0,0 +1,18 @@
+
+
+
diff --git a/resources/images/icons/discussion.svg b/resources/images/icons/discussion.svg
new file mode 100644
index 00000000..e529f5e2
--- /dev/null
+++ b/resources/images/icons/discussion.svg
@@ -0,0 +1,8 @@
+
+
diff --git a/resources/images/icons/download.svg b/resources/images/icons/download.svg
new file mode 100644
index 00000000..7cdc0067
--- /dev/null
+++ b/resources/images/icons/download.svg
@@ -0,0 +1 @@
+
diff --git a/resources/images/icons/edit.svg b/resources/images/icons/edit.svg
new file mode 100644
index 00000000..9577b4f6
--- /dev/null
+++ b/resources/images/icons/edit.svg
@@ -0,0 +1 @@
+
diff --git a/resources/images/icons/eye.svg b/resources/images/icons/eye.svg
new file mode 100644
index 00000000..dc7fce7c
--- /dev/null
+++ b/resources/images/icons/eye.svg
@@ -0,0 +1,8 @@
+
+
diff --git a/resources/images/icons/eyeClosed.svg b/resources/images/icons/eyeClosed.svg
new file mode 100644
index 00000000..e71ec4b6
--- /dev/null
+++ b/resources/images/icons/eyeClosed.svg
@@ -0,0 +1,8 @@
+
+
diff --git a/resources/images/icons/history.svg b/resources/images/icons/history.svg
new file mode 100644
index 00000000..d7567150
--- /dev/null
+++ b/resources/images/icons/history.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/icons/history_white.svg b/resources/images/icons/history_white.svg
new file mode 100644
index 00000000..ef734dd3
--- /dev/null
+++ b/resources/images/icons/history_white.svg
@@ -0,0 +1 @@
+
diff --git a/resources/images/icons/home.svg b/resources/images/icons/home.svg
new file mode 100644
index 00000000..ff37d418
--- /dev/null
+++ b/resources/images/icons/home.svg
@@ -0,0 +1 @@
+
diff --git a/resources/images/icons/image.svg b/resources/images/icons/image.svg
new file mode 100644
index 00000000..0d0a198e
--- /dev/null
+++ b/resources/images/icons/image.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/resources/images/icons/info.svg b/resources/images/icons/info.svg
new file mode 100644
index 00000000..973702e0
--- /dev/null
+++ b/resources/images/icons/info.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/resources/images/icons/link.svg b/resources/images/icons/link.svg
new file mode 100644
index 00000000..581d0e37
--- /dev/null
+++ b/resources/images/icons/link.svg
@@ -0,0 +1,8 @@
+
+
diff --git a/resources/images/icons/lock.svg b/resources/images/icons/lock.svg
new file mode 100644
index 00000000..d6fbab6d
--- /dev/null
+++ b/resources/images/icons/lock.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/resources/images/icons/logOut.svg b/resources/images/icons/logOut.svg
new file mode 100644
index 00000000..98ca7b4d
--- /dev/null
+++ b/resources/images/icons/logOut.svg
@@ -0,0 +1,8 @@
+
+
diff --git a/resources/images/icons/login.svg b/resources/images/icons/login.svg
new file mode 100644
index 00000000..cac442ce
--- /dev/null
+++ b/resources/images/icons/login.svg
@@ -0,0 +1 @@
+
diff --git a/resources/images/icons/more.svg b/resources/images/icons/more.svg
new file mode 100644
index 00000000..b4e2b0e2
--- /dev/null
+++ b/resources/images/icons/more.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/icons/move.svg b/resources/images/icons/move.svg
new file mode 100644
index 00000000..ddbf49b9
--- /dev/null
+++ b/resources/images/icons/move.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/resources/images/icons/profile.svg b/resources/images/icons/profile.svg
new file mode 100644
index 00000000..4147ed40
--- /dev/null
+++ b/resources/images/icons/profile.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/icons/random.svg b/resources/images/icons/random.svg
new file mode 100644
index 00000000..1792476e
--- /dev/null
+++ b/resources/images/icons/random.svg
@@ -0,0 +1 @@
+
diff --git a/resources/images/icons/reddit.svg b/resources/images/icons/reddit.svg
new file mode 100644
index 00000000..21726516
--- /dev/null
+++ b/resources/images/icons/reddit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/icons/settings.svg b/resources/images/icons/settings.svg
new file mode 100644
index 00000000..fbc1f5b4
--- /dev/null
+++ b/resources/images/icons/settings.svg
@@ -0,0 +1 @@
+
diff --git a/resources/images/icons/specialpages.svg b/resources/images/icons/specialpages.svg
new file mode 100644
index 00000000..efb3b249
--- /dev/null
+++ b/resources/images/icons/specialpages.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/icons/unLock.svg b/resources/images/icons/unLock.svg
new file mode 100644
index 00000000..2893b6ca
--- /dev/null
+++ b/resources/images/icons/unLock.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/resources/images/icons/upload.svg b/resources/images/icons/upload.svg
new file mode 100644
index 00000000..be053d27
--- /dev/null
+++ b/resources/images/icons/upload.svg
@@ -0,0 +1,8 @@
+
+
diff --git a/resources/images/icons/userAvatar.svg b/resources/images/icons/userAvatar.svg
new file mode 100644
index 00000000..3f47c477
--- /dev/null
+++ b/resources/images/icons/userAvatar.svg
@@ -0,0 +1,8 @@
+
+
diff --git a/resources/images/icons/userNormal.svg b/resources/images/icons/userNormal.svg
new file mode 100644
index 00000000..b63e9612
--- /dev/null
+++ b/resources/images/icons/userNormal.svg
@@ -0,0 +1 @@
+
diff --git a/resources/images/icons/userTalk.svg b/resources/images/icons/userTalk.svg
new file mode 100644
index 00000000..990cfc39
--- /dev/null
+++ b/resources/images/icons/userTalk.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/resources/images/icons/ve-edit.svg b/resources/images/icons/ve-edit.svg
new file mode 100644
index 00000000..fa13e4d4
--- /dev/null
+++ b/resources/images/icons/ve-edit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/icons/warning.svg b/resources/images/icons/warning.svg
new file mode 100644
index 00000000..53c5d5ba
--- /dev/null
+++ b/resources/images/icons/warning.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/icons/watchlist.svg b/resources/images/icons/watchlist.svg
new file mode 100644
index 00000000..d974bb46
--- /dev/null
+++ b/resources/images/icons/watchlist.svg
@@ -0,0 +1 @@
+
diff --git a/resources/images/icons/whatlinkshere.svg b/resources/images/icons/whatlinkshere.svg
new file mode 100644
index 00000000..f4e3214d
--- /dev/null
+++ b/resources/images/icons/whatlinkshere.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/resources/main.js b/resources/main.js
new file mode 100644
index 00000000..a9f01d07
--- /dev/null
+++ b/resources/main.js
@@ -0,0 +1 @@
+/* JavaScript for the Example skin */
diff --git a/resources/mixins.less b/resources/mixins.less
new file mode 100644
index 00000000..e7b34a2b
--- /dev/null
+++ b/resources/mixins.less
@@ -0,0 +1,154 @@
+/*
+ * Citizen - Mixins
+ * https://starcitizen.tools
+*/
+
+// To hide objects, but keep them accessible for screen-readers
+.hidden() {
+ position: absolute;
+ top: -9999px;
+}
+
+// Set content to be at the center
+.content-center() {
+ max-width: @page-width;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+// Prepare for RL icons
+.resource-loader-icon-link() {
+ width: @icon-box-size;
+ height: @icon-box-size;
+ display: block;
+}
+
+.resource-loader-icon-link-small() {
+ width: @icon-size;
+ height: @icon-size;
+ display: block;
+}
+
+.resource-loader-icon() {
+ content: "";
+ position: absolute;
+ width: inherit;
+ height: inherit;
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+.resource-loader-menu-icon() {
+ content: "";
+ position: absolute;
+ width: @icon-box-size;
+ height: @icon-box-size;
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+.resource-loader-list-icon() {
+ content: "";
+ position: relative;
+ flex-shrink: 0;
+ align-self: center;
+ width: @icon-size;
+ height: @icon-size;
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+// Material-like menu
+.menu-container() {
+ padding: 8px 0;
+ background-color: @menu-background;
+ display: block;
+}
+
+.menu-item-link() {
+ display: flex;
+ color: @color-item-text!important;
+ transition: @transition-background-quick, @transition-color-quick;
+}
+
+.menu-item-link-hover() {
+ color: @color-item-text-hover!important;
+ background-color: @menu-item-link-hover;
+}
+
+.menu-item-link-active() {
+ color: @color-item-text-hover!important;
+ background-color: @menu-item-link-active;
+}
+
+.menu-item-link-focus() {
+ color: @color-item-text-hover!important;
+ background-color: @menu-item-link-hover;
+}
+
+// Button Styles
+.button-blue() {
+ background-color: @accent-50;
+ color: @base-80!important;
+ transition: @transition-background-quick, @transition-color-quick;
+}
+
+.button-blue-active() {
+ background-color: @accent-10;
+ color: @base-100!important;
+}
+
+.button-red() {
+ background-color: @red-50;
+ color: @base-80!important;
+ transition: @transition-background-quick, @transition-color-quick;
+}
+
+.button-red-active() {
+ background-color: @red-30;
+ color: @base-100!important;
+}
+
+// Link styles
+.link-content-text(@color) {
+ background-image: linear-gradient(to right, @color 0, @color 100%);
+ background-repeat: no-repeat;
+ background-size: 0 100%;
+ transition: @transition-background-quick, @transition-color-quick;
+
+ &:hover, &:active, &:focus {
+ outline: none;
+ color: white!important;
+ background-size: 100% 100%!important;
+ }
+}
+
+// Gradient overflow
+.gradient-overflow(@position) {
+ content: "";
+ position: fixed;
+ z-index: 3;
+ width: 100%;
+ height: @margin-side * 2;
+ background: @position;
+}
+
+// Box-shadow
+// https://codepen.io/sdthornton/pen/wBZdXq
+.boxshadow(@level: 1){
+ & when (@level = 1) {
+ box-shadow: 0 1px 3px rgba(0,0,0,0.03), 0 1px 2px rgba(0,0,0,0.06); // Cards
+ }
+ & when (@level = 2) {
+ box-shadow: 0 3px 6px rgba(0,0,0,0.04), 0 3px 6px rgba(0,0,0,0.0575);
+ }
+ & when (@level = 3) {
+ box-shadow: 0 10px 20px rgba(0,0,0,0.0475), 0 6px 6px rgba(0,0,0,0.0575); // Drawer
+ }
+ & when (@level = 4) {
+ box-shadow: 0 14px 28px rgba(0,0,0,0.0625), 0 10px 10px rgba(0,0,0,0.055); // FAB, floating elements
+ }
+ & when (@level = 5) {
+ box-shadow: 0 19px 38px rgba(0,0,0,0.075), 0 15px 12px rgba(0,0,0,0.055); // Dialogs
+ }
+}
diff --git a/resources/print.less b/resources/print.less
new file mode 100644
index 00000000..7b23cc38
--- /dev/null
+++ b/resources/print.less
@@ -0,0 +1,9 @@
+/* Styles for print view and printing */
+
+/* Hide some extra stuff from print view (some of the navigation is already hidden automatically, but not all) */
+.mw-indicators,
+#p-logo,
+#catlinks,
+#mw-footer {
+ display: none;
+}
diff --git a/resources/screen.less b/resources/screen.less
new file mode 100644
index 00000000..c71c56ad
--- /dev/null
+++ b/resources/screen.less
@@ -0,0 +1,11 @@
+/* Citizen */
+
+@import 'variables.less';
+@import 'mixins.less';
+@import 'components/common.less';
+@import 'components/navigation.less';
+@import 'components/wikitable.less';
+@import 'components/page-tools.less';
+@import 'components/search.less';
+@import 'components/footer.less';
+@import 'components/bottombar.less';
diff --git a/resources/scripts/Header.js b/resources/scripts/Header.js
new file mode 100644
index 00000000..0a5e44fb
--- /dev/null
+++ b/resources/scripts/Header.js
@@ -0,0 +1,42 @@
+/*
+* Scroll up Header
+* Modified from https://codepen.io/sajjad/pen/vgEZNy
+*/
+
+// Hide header on scroll down
+var didScroll;
+var lastScrollTop = 0;
+var delta = 5;
+var navbarHeight = $('.mw-header-container').outerHeight();
+
+$(window).scroll(function(event){
+ didScroll = true;
+});
+
+setInterval(function() {
+ if (didScroll) {
+ hasScrolled();
+ didScroll = false;
+ }
+}, 250);
+
+function hasScrolled() {
+ var st = $(this).scrollTop();
+
+ // Make scroll more than delta
+ if(Math.abs(lastScrollTop - st) <= delta)
+ return;
+
+ // If scrolled down and past the navbar, add class .nav-up.
+ if (st > lastScrollTop && st > navbarHeight){
+ // Scroll Down
+ $('header').removeClass('nav-down').addClass('nav-up');
+ } else {
+ // Scroll Up
+ if(st + $(window).height() < $(document).height()) {
+ $('header').removeClass('nav-up').addClass('nav-down');
+ }
+ }
+
+ lastScrollTop = st;
+}
diff --git a/resources/scripts/ToC.js b/resources/scripts/ToC.js
new file mode 100644
index 00000000..e69de29b
diff --git a/resources/variables.less b/resources/variables.less
new file mode 100644
index 00000000..63f42e0b
--- /dev/null
+++ b/resources/variables.less
@@ -0,0 +1,169 @@
+/*
+ * Citizen - Navigation Styles
+ * https://starcitizen.tools
+*/
+
+// Import MediaWiki-supplied mixins
+@import "mediawiki.mixins";
+
+/*
+ * Framework
+*/
+@html-font-size: 100%;
+@margin-side: 20px;
+@page-width: 860px;
+@footer-width: 1290px;
+@icon-size: 18px;
+@icon-box-size: @icon-size + @icon-margin * 2;
+@icon-margin: 3px;
+@icon-padding: 15px;
+@header-height: 56px;
+@sidebar-sitename-height: 69px;
+@drawer-width: 300px;
+@padding-menu-item: 10px 15px;
+@padding-menu-item-big: 10px 20px;
+@negative-margin: (@footer-width - @page-width) / -2;
+
+/*
+ * Colors
+*/
+
+// Wikimedia colors
+@base-0: #000;
+@base-10: #222;
+@base-20: #54595d;
+@base-30: #72777d;
+@base-50: #a2a9b1;
+@base-80: #eaecf0;
+@base-90: #f8f9fa;
+@base-100: #fff;
+
+@accent-10: #2a4b8d;
+@accent-50: #36c;
+
+@red-30: #b32424;
+@red-50: #d33;
+@red-90: #fee7e6;
+
+@green-30: #14866d;
+@green-50: #00af89;
+@green-90: #d5fdf4;
+
+@yellow-30: #ac6600;
+@yellow-50: #fc3;
+@yellow-90: #fef6e7;
+
+@menu-background: @base-100;
+@opacity-icon: 0.3;
+@opacity-icon-active: 0.5;
+@color-item-text: @base-30;
+@color-item-text-hover: @base-20;
+@color-item-header: @base-10;
+@menu-item-link-hover: @base-90;
+@menu-item-link-active: @base-80;
+
+@color-content-text: @base-20;
+@color-content-header: @base-10;
+@color-content-caption: @base-30;
+
+@color-link: #3366cc;
+@color-link-active: #5b84d6;
+@color-link-new: #dd3333;
+@color-link-new-active: #e35b5b;
+@color-link-visited: #6b4ba1;
+@color-link-visited-active: #886eb3;
+
+// Citizen colors
+@color-footer-background-40: #0f1418;
+@color-footer-background-50: #131a21;
+@color-footer-background-60: #1a252d;
+@color-footer-text: #979c9f;
+@color-footer-link: #cfdee8;
+@color-footer-link-active: @base-80;
+/*
+
+/*
+ * Transitions
+*/
+@transition-color-quick: color 0.2s ease;
+@transition-transform: transform 0.5s cubic-bezier(0.77,0.2,0.05,1.0);
+@transition-transform-quick: transform 0.2s cubic-bezier(0.77,0.2,0.05,1.0);
+@transition-width: width 0.5s cubic-bezier(0.77,0.2,0.05,1.0);
+@transition-height: height 0.5s cubic-bezier(0.77,0.2,0.05,1.0);
+@transition-background-quick: background 0.2s ease;
+@transition-background: background 0.55s ease;
+@transition-opacity: opacity 0.55s ease;
+@transition-opacity-quick: opacity 0.2s ease;
+@transition-border: border 0.55s ease;
+@transition-border-quick: border 0.2s ease;
+@transition-box-shadow: box-shadow 0.55s ease;
+@transition-box-shadow-quick: box-shadow 0.2s ease;
+@transition-border-color: border-color 0.55s ease;
+
+/*
+ * Fonts
+*/
+/**
+ * System font stack for sans-serif fonts
+ *
+ * `Roboto` – Default font, Android 4.0+ system font, OFL licensed
+ * `-apple-system` ('San Francisco' font) – Support Safari 9+ macOS and iOS, Firefox macOS
+ * `BlinkMacSystemFont` ('San Francisco' font) – Chrome 48+ macOS and iOS
+ * `Segoe UI` – Windows Vista & newer
+ * `Oxygen` - Linux, KDE
+ * `Ubuntu` - Linux, Ubuntu
+ * `Cantarell` - Linux, GNOME
+ * `Helvetica, Arial, sans-serif` – (Generic) Web fallback
+ * Note that standard `system-ui` value has resulted in unresolved side-effects in certain OS/language combinations as of now and is therefore not included.
+ */
+@fonts: 'Roboto', -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", "Ubuntu", "Cantarell", "Helvetica Neue", sans-serif;
+/**
+ * Citizen font stack
+ *
+ * `Titillium Web` – Futuristic, Closet to Univia Pro, OFL licensed
+ * `Univia Pro` - Fallback font if Univia Pro is present
+ * 'Monoid' - Code font, OFL licensed
+ * Fallback using primary fonts
+ */
+@fonts-secondary: 'Titillium Web', 'Univia Pro', @fonts;
+/**
+ * System font stack for monospace fonts
+ *
+ * `Menlo` – macOS 10.6+
+ * `Roboto Mono` - Android 4.0+
+ * `Consolas` – Windows Vista & newer
+ * `Liberation Mono` – Fedora, Ubuntu, … OFL licensed
+ * `'Courier New', monospace` – (Generic) web font fallback
+ */
+@fonts-monospace: 'Menlo', 'Roboto Mono', 'Consolas', 'Liberation Mono', 'Courier New', monospace;
+
+/*
+ * Text hierarchy
+*/
+
+// Scaling for content text
+@content-scaling: 0.625;
+@toc-scaling: 0.75;
+
+// Wikimedia scale - https://design.wikimedia.org/style-guide/visual-style_typography.html
+@content-h1-size: 3.2rem * @content-scaling;
+@content-h2-size: 2.4rem * @content-scaling;
+@content-h3-size: 2.0rem * @content-scaling;
+@content-h4-size: 1.8rem * @content-scaling;
+@content-h5-size: 1.6rem * @content-scaling;
+@content-h6-size: 1.6rem * @content-scaling;
+@content-body-size: 1.6rem * @content-scaling;
+@content-lead-paragraph-size: 1.8rem * @content-scaling;
+@content-quote-primary-size: 2.0rem * @content-scaling;
+@content-quote-secondary-size: 1.4rem * @content-scaling;
+@content-caption-size: 1.3rem * @content-scaling;
+@content-small-text-size: 1.3rem * @content-scaling;
+@content-monospace-size: 1.4rem * @content-scaling;
+@content-line-height: 1.6;
+@content-margin-top: 0.8rem;
+
+@ui-menu-text: 14px;
+@ui-menu-text-big: 15px;
+@ui-menu-header: 16px;
+
+@footer-line-height: 1.4;
diff --git a/skin.json b/skin.json
new file mode 100644
index 00000000..98499baa
--- /dev/null
+++ b/skin.json
@@ -0,0 +1,181 @@
+{
+ "name": "Citizen",
+ "namemsg": "skinname-citizen",
+ "author": "alistair3149",
+ "url": "https://starcitizen.tools",
+ "descriptionmsg": "citizen-desc",
+ "namemsg": "skinname-citizen",
+ "license-name": "CC-BY-SA-4.0",
+ "type": "skin",
+ "requires": {
+ "MediaWiki": ">= 1.31.0"
+ },
+ "ValidSkinNames": {
+ "citizen": "Citizen"
+ },
+ "MessagesDirs": {
+ "Citizen": [
+ "i18n"
+ ]
+ },
+ "ResourceModules": {
+ "skins.citizen": {
+ "class": "ResourceLoaderSkinModule",
+ "styles": {
+ "resources/screen.less": {
+ "media": "screen"
+ },
+ "resources/print.less": {
+ "media": "print"
+ },
+ "resources/font-face.less": {}
+ }
+ },
+ "skins.citizen.js": {
+ "scripts": [
+ "resources/main.js",
+ "resources/scripts/Header.js"
+ ]
+ },
+ "skins.citizen.icons": {
+ "class": "ResourceLoaderImageModule",
+ "selector": "#citizen-ui-{name} > *:after",
+ "defaultColor": "#000",
+ "useDataURI": false,
+ "images": {
+ "discord": "resources/images/icons/discord.svg"
+ }
+ },
+ "skins.citizen.icons.ca": {
+ "class": "ResourceLoaderImageModule",
+ "selector": "#ca-{name} > *:after",
+ "defaultColor": "#000",
+ "useDataURI": false,
+ "images": {
+ "view": "resources/images/icons/article.svg",
+ "ve-edit": "resources/images/icons/ve-edit.svg",
+ "edit": "resources/images/icons/edit.svg",
+ "history": "resources/images/icons/history.svg",
+ "delete": "resources/images/icons/delete.svg",
+ "move": "resources/images/icons/move.svg",
+ "protect": "resources/images/icons/lock.svg",
+ "unprotect": "resources/images/icons/unLock.svg",
+ "watch": "resources/images/icons/eye.svg",
+ "unwatch": "resources/images/icons/eyeClosed.svg",
+ "talk": "resources/images/icons/discussion.svg",
+ "nstab-main": "resources/images/icons/article.svg"
+ }
+ },
+ "skins.citizen.icons.p": {
+ "class": "ResourceLoaderImageModule",
+ "selector": "#p-{name} > *:before",
+ "defaultColor": "#000",
+ "useDataURI": false,
+ "images": {
+ "actions": "resources/images/icons/more.svg"
+ }
+ },
+ "skins.citizen.icons.toc": {
+ "class": "ResourceLoaderImageModule",
+ "selector": ".toctoggle > a:before",
+ "defaultColor": "#000",
+ "useDataURI": false,
+ "images": {
+ "": "resources/images/icons/ToC.svg"
+ }
+ },
+ "skins.citizen.icons.es": {
+ "class": "ResourceLoaderImageModule",
+ "selector": ".mw-editsection > a{name}:before",
+ "defaultColor": "#000",
+ "useDataURI": false,
+ "images": {
+ "": "resources/images/icons/edit.svg",
+ ".mw-editsection-visualeditor": "resources/images/icons/ve-edit.svg"
+ }
+ },
+ "skins.citizen.icons.n": {
+ "class": "ResourceLoaderImageModule",
+ "selector": "#n-{name} > *:after",
+ "defaultColor": "#000",
+ "useDataURI": false,
+ "images": {
+ "mainpage-description": "resources/images/icons/home.svg",
+ "recentchanges": "resources/images/icons/history.svg",
+ "randompage": "resources/images/icons/random.svg"
+ }
+ },
+ "skins.citizen.icons.t": {
+ "class": "ResourceLoaderImageModule",
+ "selector": "#t-{name} > *:after",
+ "defaultColor": "#000",
+ "useDataURI": false,
+ "images": {
+ "whatlinkshere": "resources/images/icons/whatlinkshere.svg",
+ "recentchangeslinked": "resources/images/icons/history.svg",
+ "upload": "resources/images/icons/upload.svg",
+ "specialpages": "resources/images/icons/specialpages.svg",
+ "print": "resources/images/icons/download.svg",
+ "permalink": "resources/images/icons/link.svg",
+ "info": "resources/images/icons/info.svg"
+ }
+ },
+ "skins.citizen.icons.pt": {
+ "class": "ResourceLoaderImageModule",
+ "selector": "#pt-{name} > *:after",
+ "defaultColor": "#000",
+ "useDataURI": false,
+ "images": {
+ "userpage": "resources/images/icons/userNormal.svg",
+ "mytalk": "resources/images/icons/userTalk.svg",
+ "preferences": "resources/images/icons/settings.svg",
+ "betafeatures": "resources/images/icons/beta.svg",
+ "watchlist": "resources/images/icons/watchlist.svg",
+ "mycontris": "resources/images/icons/contributions.svg",
+ "logout": "resources/images/icons/logOut.svg",
+ "createaccount": "resources/images/icons/userAvatar.svg",
+ "login": "resources/images/icons/logOut.svg"
+ }
+ },
+ "skins.citizen.icons.footer": {
+ "class": "ResourceLoaderImageModule",
+ "selector": "#footer #{name}:before",
+ "defaultColor": "#fff",
+ "useDataURI": false,
+ "images": {
+ "lastmod": "resources/images/icons/history_white.svg"
+ }
+ },
+ "skins.citizen.icons.badges": {
+ "class": "ResourceLoaderImageModule",
+ "selector": "#footer-{name}ico a:before",
+ "defaultColor": "#fff",
+ "useDataURI": false,
+ "images": {
+ "copyright": "resources/images/badges/CCBYSA4.svg",
+ "poweredby": "resources/images/badges/PoweredByMediawiki.svg",
+ "monitoredby": "resources/images/badges/MonitoredByWikiApiary.svg",
+ "gdprcompliance": "resources/images/badges/GDPR.svg",
+ "madeby": "resources/images/badges/MadeByTheCommunity.svg"
+ }
+ }
+ },
+ "ResourceFileModulePaths": {
+ "localBasePath": "",
+ "remoteSkinPath": "Citizen"
+ },
+ "ResourceModuleSkinStyles": {
+ "citizen": {
+ "+ext.echo.styles.badge": "skinStyles/echo.styles.badge.less",
+ "+ext.relatedArticles.cards": "skinStyles/ext.relatedArticles.cards.less",
+ "+ext.relatedArticles.readMore": "skinStyles/ext.relatedArticles.readMore.less",
+ "+ext.uls.pt": "skinStyles/ext.uls.pt.less",
+ "+ext.visualEditor.core": "skinStyles/ext.visualEditor.core.less"
+ }
+ },
+ "AutoloadClasses": {
+ "SkinCitizen": "includes/SkinCitizen.php",
+ "CitizenTemplate": "includes/CitizenTemplate.php"
+ },
+ "manifest_version": 1
+}
diff --git a/skinStyles/echo.styles.badge.less b/skinStyles/echo.styles.badge.less
new file mode 100644
index 00000000..a8fe6078
--- /dev/null
+++ b/skinStyles/echo.styles.badge.less
@@ -0,0 +1,58 @@
+/*
+ * Citizen - Echo Styles
+ * https://starcitizen.tools
+*/
+
+@import '../resources/variables.less';
+
+// Styles for echo badges
+#pt-notifications {
+ &-alert,
+ &-notice {
+
+ .mw-echo-notifications-badge {
+ top: -1000px; // Default was -1005px for some reason
+ margin: 0;
+ width: @icon-box-size + @icon-padding;
+ height: @header-height;
+
+ &:hover,
+ &:focus,
+ &:active {
+ &:before {
+ opacity: 0.6!important; // override all styles
+ }
+ }
+
+ // Icon tweaks
+ &:before {
+ opacity: 0.4;
+ background-position: center;
+ transition: @transition-opacity, @transition-transform;
+ }
+
+ // Badge tweaks
+ &:after {
+ background-color: @red-50;
+ font-size: 11px;
+ }
+
+ &.mw-echo-notifications-badge-all-read {
+ opacity: 0.75; // 0.75 * 0.4 = 0.3
+ }
+ }
+ }
+
+ // Rotate bell
+ &-alert {
+ .mw-echo-notifications-badge {
+ &:hover,
+ &:focus,
+ &:active {
+ &:before {
+ transform: rotateZ(20deg);
+ }
+ }
+ }
+ }
+}
diff --git a/skinStyles/ext.relatedArticles.cards.less b/skinStyles/ext.relatedArticles.cards.less
new file mode 100644
index 00000000..0aad0c6d
--- /dev/null
+++ b/skinStyles/ext.relatedArticles.cards.less
@@ -0,0 +1,28 @@
+/*
+ * Citizen - Related Articles Styles
+ * https://starcitizen.tools
+*/
+
+@import '../resources/variables.less';
+@import '../resources/mixins.less';
+
+.ext-related-articles-card-list {
+ margin-top: 0;
+ padding-top: @content-margin-top; // Give room for shadows and translate
+
+ .ext-related-articles-card {
+ border: 0!important;
+ border-radius: 0!important;
+ .boxshadow(1);
+ transition: @transition-transform-quick;
+
+ &:hover {
+ .boxshadow(2);
+ transform:translateY(-2px);
+ }
+
+ > a:hover {
+ box-shadow: none;
+ }
+ }
+}
diff --git a/skinStyles/ext.relatedArticles.readMore.less b/skinStyles/ext.relatedArticles.readMore.less
new file mode 100644
index 00000000..aab3e3bd
--- /dev/null
+++ b/skinStyles/ext.relatedArticles.readMore.less
@@ -0,0 +1,21 @@
+/*
+ * Citizen - Related Articles Styles
+ * https://starcitizen.tools
+*/
+
+@import '../resources/variables.less';
+
+.read-more-container {
+ margin: 0 auto;
+ max-width: @footer-width;
+}
+
+.ra-read-more {
+ padding: @margin-side;
+
+ h2 {
+ margin: 0;
+ padding: 0;
+ font-size: @content-caption-size; // In line with other styles
+ }
+}
diff --git a/skinStyles/ext.uls.pt.less b/skinStyles/ext.uls.pt.less
new file mode 100644
index 00000000..5111454b
--- /dev/null
+++ b/skinStyles/ext.uls.pt.less
@@ -0,0 +1,25 @@
+/*
+ * Citizen - ULS toggle Styles
+ * https://starcitizen.tools
+*/
+
+@import '../resources/variables.less';
+
+// Styles for ULS icon
+#pt-uls a.uls-trigger {
+ padding: 0;
+ display: block;
+ width: @icon-box-size + @icon-padding;
+ height: @header-height;
+ color: transparent; // Hide text
+ opacity: 0.6; // Replicate color of other icons
+ background-size: @icon-box-size;
+ background-position: center;
+ transition: @transition-opacity;
+
+ &:hover,
+ &:focus,
+ &:active {
+ opacity: 0.8;
+ }
+}
diff --git a/skinStyles/ext.visualEditor.core.less b/skinStyles/ext.visualEditor.core.less
new file mode 100644
index 00000000..f74a4d84
--- /dev/null
+++ b/skinStyles/ext.visualEditor.core.less
@@ -0,0 +1,53 @@
+/*
+ * Citizen - Visual Editor Styles
+ * https://starcitizen.tools
+*/
+
+@import '../resources/variables.less';
+
+.ve-activated {
+ #mw-sidebar-sitename {
+ top: @sidebar-sitename-height + @header-height * 2 + @margin-side;
+ }
+
+ #page-tools {
+ z-index: 1; // Fix collision
+ pointer-events: auto;
+ opacity: 1;
+ }
+}
+
+// Fix weird compressed text bug
+.ve-init-mw-tempWikitextEditorWidget {
+ width: 100%!important;
+}
+
+// Match animation
+.ve-init-mw-desktopArticleTarget-toolbar-open > .oo-ui-toolbar-bar {
+ transition: @transition-transform!important;
+}
+
+// Make toolbar more mobile friendly, well at least I did by best
+.ve-init-mw-desktopArticleTarget-toolbar {
+ margin: -20px -20px 20px -20px!important; // Bypass @media screen styles
+}
+
+// Stick with the header
+.nav-down ~ .mw-body {
+ .ve-ui-toolbar-floating > .oo-ui-toolbar-bar {
+ transform: translateY( @header-height );
+ }
+}
+
+.ve-ui-surface-placeholder, .ve-ui-surface .ve-ce-documentNode {
+ padding: 0;
+}
+
+.ve-init-mw-desktopArticleTarget .CodeMirror {
+ padding: 0;
+ line-height: @content-line-height; // Lining up syntax highlighter
+}
+
+.ve-init-mw-desktopArticleTarget .CodeMirror-code *, .ve-init-mw-desktopArticleTarget .ve-ui-mwWikitextSurface .ve-ce-paragraphNode {
+ line-height: @content-line-height; // Lining up syntax highlighter
+}