// Styles key sets render blocking styles
// Unlike other keys in this definition it is an associative array
// where each key is the group name and points to a list of modules
- 'styles' => [],
+ 'styles' => [
+ 'content' => [],
+ ],
// modules not specific to any specific skin or page
'core' => [
// Enforce various default modules for all pages and all skins
// Preload jquery.tablesorter for mediawiki.page.ready
if ( strpos( $out->getHTML(), 'sortable' ) !== false ) {
$modules['content'][] = 'jquery.tablesorter';
+ $modules['styles']['content'][] = 'jquery.tablesorter.styles';
}
// Preload jquery.makeCollapsible for mediawiki.page.ready
if ( strpos( $out->getHTML(), 'mw-collapsible' ) !== false ) {
$modules['content'][] = 'jquery.makeCollapsible';
+ $modules['styles']['content'][] = 'jquery.makeCollapsible.styles';
}
if ( $out->isTOCEnabled() ) {
],
],
+ 'jquery.tablesorter.styles' => [
+ 'targets' => [ 'desktop', 'mobile' ],
+ 'styles' => [
+ 'resources/src/jquery/jquery.tablesorter.styles.less',
+ ],
+ ],
+ 'jquery.makeCollapsible.styles' => [
+ 'targets' => [ 'desktop', 'mobile' ],
+ 'class' => ResourceLoaderLessVarFileModule::class,
+ 'styles' => [
+ 'resources/src/jquery/jquery.makeCollapsible.styles.less',
+ ],
+ ],
+
'mediawiki.skinning.content.parsoid' => [
// Style Parsoid HTML+RDFa output consistent with wikitext from PHP parser
// with the interface.css styles; skinStyles should be used if your
'scripts' => 'resources/src/jquery/jquery.localize.js',
],
'jquery.makeCollapsible' => [
+ 'dependencies' => [ 'jquery.makeCollapsible.styles' ],
'scripts' => 'resources/src/jquery/jquery.makeCollapsible.js',
'styles' => 'resources/src/jquery/jquery.makeCollapsible.css',
'messages' => [ 'collapsible-expand', 'collapsible-collapse' ],
'styles' => 'resources/src/jquery/jquery.tablesorter.less',
'messages' => [ 'sort-descending', 'sort-ascending' ],
'dependencies' => [
+ 'jquery.tablesorter.styles',
'mediawiki.RegExp',
'mediawiki.language.months',
],
+/*
+ * Please do not add any CSS rules here that impact the positioning of the element
+ * e.g. padding, margin, position or float.
+ * These instead should live in jquery.makeCollapsible.styles
+*/
+
/* See also jquery.makeCollapsible.js */
.mw-collapsible-toggle {
float: right;
.mw-collapsible-toggle-default:after {
content: ']';
}
-/* Align the toggle based on the direction of the content language */
-/* @noflip */
-.mw-content-ltr .mw-collapsible-toggle,
-.mw-content-rtl .mw-content-ltr .mw-collapsible-toggle {
- float: right;
-}
-/* @noflip */
-.mw-content-rtl .mw-collapsible-toggle,
-.mw-content-ltr .mw-content-rtl .mw-collapsible-toggle {
- float: left;
-}
.mw-customtoggle,
.mw-collapsible-toggle {
.mw-content-ltr .mw-content-rtl caption .mw-collapsible-toggle {
float: none;
}
-
-/* list-items go as wide as their parent element, don't float them inside list items */
-li .mw-collapsible-toggle,
-.mw-content-ltr li .mw-collapsible-toggle,
-.mw-content-rtl li .mw-collapsible-toggle,
-.mw-content-rtl .mw-content-ltr li .mw-collapsible-toggle,
-.mw-content-ltr .mw-content-rtl li .mw-collapsible-toggle {
- float: none;
-}
-
-/* the added list item should have no list-style */
-.mw-collapsible-toggle-li {
- list-style: none;
-}
/**
* jQuery makeCollapsible
+ * Note: To avoid performance issues such as reflows, several styles are
+ * shipped in mediawiki.makeCollapsible.styles to reserve space for the toggle control. Please
+ * familiarise yourself with that CSS before making any changes to this code.
*
* Dual licensed:
* - CC BY 3.0 <http://creativecommons.org/licenses/by/3.0>
if ( $collapsible.data( 'mw-made-collapsible' ) ) {
return;
} else {
- $collapsible.data( 'mw-made-collapsible', true );
+ // Let CSS know that it no longer needs to worry about flash of unstyled content.
+ // This will allow mediawiki.makeCollapsible.styles to disable temporary pseudo elements, that
+ // are needed to avoid a flash of unstyled content.
+ $collapsible.addClass( 'mw-made-collapsible' )
+ .data( 'mw-made-collapsible', true );
}
// Use custom text or default?
--- /dev/null
+/**
+ * These rules prevent re-flows relating to collapsible on-wiki elements (T42812).
+ * This is done by temporarily creating a pseudo element in the place that JavaScript will insert
+ * a toggle control. The same CSS rules that control the positioning of the toggle control will apply
+ * to the pseudo element. When the JavaScript has executed
+ * (See corresponding non-render blocking CSS in jquery.makeCollapsible)
+ * all pseudo elements will be removed.
+ *
+ * Currently we support all the examples on [[mw:Manual:Collapsible_elements/Demo/Simple]]
+ * All examples on [[mw:Manual:Collapsible_elements/Demo/Advanced]] are supported with the following
+ * exceptions
+ * - Custom collapsible 4 (table-row)
+ * -- CSS selector would be too complicated
+ * - Collapsible div nested in collapsed div
+ * -- Given it's not easy to identify the collapsed content via CSS, text will be shown until
+ * JavaScript has loaded
+ * - "Combination example"
+ * -- At a later time, we may want to support the use of of `attr`, but given the code
+ * complexity we will not for the time being (see https://davidwalsh.name/css-content-attr)
+ */
+
+// This selector is used frequently in the code to indicate that the JavaScript has successfully completed
+// it's execution and pseudo elements can be disabled. For readability and maintainability it is separated
+// as a LESS variable.
+@exclude: ~'.mw-made-collapsible';
+
+.client-js {
+
+ ol.mw-collapsible:before,
+ ul.mw-collapsible:before,
+ .mw-collapsible-toggle-li {
+ /*
+ Rather than inherit any margins from the the general li selector - make sure this is explicit
+ to avoid reflows
+ */
+ display: list-item;
+ list-style: none;
+ margin-bottom: 0.1em;
+ }
+
+ // Reset when mw-collapsible-toggle-li is rendered
+ ol.mw-made-collapsible:before,
+ ul.mw-made-collapsible:before {
+ display: none;
+ }
+
+ ol.mw-collapsible:not( @{exclude} ):before,
+ ul.mw-collapsible:not( @{exclude} ):before,
+ // Where the tbody or thead is the first child of the collapsible table
+ table.mw-collapsible:not( @{exclude} ) :first-child tr:first-child th:last-child:before,
+ table.mw-collapsible:not( @{exclude} ) > caption:first-child:after {
+ content: '[@{msg-collapsible-collapse}]';
+ }
+
+ td.mw-collapsed:not( @{exclude} ):before,
+ table.mw-collapsed:not( @{exclude} ) :first-child tr:first-child th:last-child:before,
+ table.mw-collapsed:not( @{exclude} ) > caption:first-child:after,
+ div.mw-collapsed:not( @{exclude} ):before {
+ content: '[@{msg-collapsible-expand}]';
+ }
+
+ // Any element with id beginning `mw-customcollapsible` will have special treatment
+ .mw-collapsible[ id^='mw-customcollapsible' ] th:before,
+ .mw-collapsible[ id^='mw-customcollapsible' ]:before {
+ content: none !important; // stylelint-disable-line declaration-no-important
+ }
+
+ // Special case for table where first child is caption element
+ table.mw-collapsible:not( @{exclude} ) > caption:first-child:after {
+ float: none;
+ display: block;
+ }
+
+ // Use the exclude selector to ensure animations do not break
+ .mw-collapsed:not( @{exclude} ) {
+ // Avoid FOUC/reflows on collapsed elements by making sure they are opened by default (T42812)
+ > p,
+ > table,
+ // Manual:Collapsible_elements/Demo/Simple#Collapsed_by_default
+ > thead + tbody,
+ tr:not( :first-child ),
+ .mw-collapsible-content {
+ display: none;
+ }
+ }
+}
+
+/* Align the toggle based on the direction of the content language */
+/* @noflip */
+.mw-content-ltr,
+.mw-content-rtl .mw-content-ltr {
+ .mw-collapsible:not( @{exclude} ) th:before,
+ .mw-collapsible:not( @{exclude} ):before,
+ .mw-collapsible-toggle {
+ float: right;
+ }
+}
+
+/* @noflip */
+.mw-content-rtl,
+.mw-content-ltr .mw-content-rtl {
+ .mw-collapsible:not( @{exclude} ) th:before,
+ .mw-collapsible:not( @{exclude} ):before,
+ .mw-collapsible-toggle {
+ float: left;
+ }
+}
+
+/* list-items go as wide as their parent element, don't float them inside list items */
+li,
+.mw-content-ltr li,
+.mw-content-rtl li,
+.mw-content-ltr .mw-content-rtl li,
+.mw-content-rtl .mw-content-ltr li {
+ .mw-collapsible-toggle {
+ float: none;
+ }
+}
+
+// special treatment for list items to match above
+// !important necessary to override overly-specific float left and right above.
+ol.mw-collapsible:not( @{exclude} ):before,
+ul.mw-collapsible:not( @{exclude} ):before {
+ float: none !important; // stylelint-disable-line declaration-no-important
+}
cursor: pointer;
background-repeat: no-repeat;
background-position: center right;
- padding-right: 21px;
+ // Note: To avoid reflows, a padding is set in
+ // the jquery.tableSorter.styles module as a render blocking style.
+ // Please do not add any CSS rules here that impact the positioning of the element
+ // e.g. padding, margin, position or float.
}
th.headerSortUp {
--- /dev/null
+.client-js {
+ // Reserve space for table sortable controls
+ table.sortable th {
+ padding-right: 21px;
+ }
+}