diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..19fdb0c --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,30 @@ +name: SCSS Linting + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + lint-scss: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '22' + + - name: Install npm dependencies + run: | + npm install + + - name: Run SCSS Linting + run: | + grunt stylelint:scss \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fed401d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +package-lock.json +node_modules/ \ No newline at end of file diff --git a/.stylelintrc b/.stylelintrc new file mode 100644 index 0000000..5185366 --- /dev/null +++ b/.stylelintrc @@ -0,0 +1,107 @@ +{ + "customSyntax": "postcss-scss", + "plugins": [ + "stylelint-csstree-validator" + ], + "rules": { + "csstree/validator": { + "syntaxExtensions": [ + "sass" + ] + }, + "at-rule-empty-line-before": [ "always", + {"except": [ "blockless-after-blockless"], ignore: ["after-comment", "inside-block"]} + ], + "at-rule-name-case": "lower", + "at-rule-name-space-after": "always-single-line", + "at-rule-no-unknown": null, # Enabled for non-scss in grunt. + "at-rule-semicolon-newline-after": "always", + "at-rule-semicolon-space-before": "never", + "block-closing-brace-newline-after": "always", + "block-closing-brace-newline-before": "always", + "block-closing-brace-space-before": "always-single-line", + "block-no-empty": true, + "block-opening-brace-newline-after": "always", + "block-opening-brace-space-after": "always-single-line", + "block-opening-brace-space-before": "always", + "color-hex-case": ["lower", { "severity": "warning" }], + "color-hex-length": ["short", { "severity": "warning" }], + "color-no-invalid-hex": true, + "declaration-bang-space-after": "never", + "declaration-bang-space-before": "always", + "declaration-block-no-duplicate-properties": true, + "declaration-block-no-shorthand-property-overrides": true, + "declaration-block-semicolon-newline-after": "always-multi-line", + "declaration-block-semicolon-space-after": "always-single-line", + "declaration-block-semicolon-space-before": "never", + "declaration-block-single-line-max-declarations": 1, + "declaration-block-trailing-semicolon": "always", + "declaration-colon-newline-after": "always-multi-line", + "declaration-colon-space-after": "always-single-line", + "declaration-colon-space-before": "never", + "declaration-no-important": null, + "font-family-no-duplicate-names": true, + "function-calc-no-unspaced-operator": true, + "function-comma-newline-after": "always-multi-line", + "function-comma-space-after": "always-single-line", + "function-comma-space-before": "never", + "function-linear-gradient-no-nonstandard-direction": true, + "function-max-empty-lines": 0, + "function-name-case": "lower", + "function-parentheses-newline-inside": "always-multi-line", + "function-parentheses-space-inside": "never-single-line", + "function-url-scheme-disallowed-list": ["data"], + "function-whitespace-after": "always", + "indentation": 4, + "keyframe-declaration-no-important": true, + "length-zero-no-unit": [true, { "severity": "warning" }], + "max-empty-lines": 2, + "max-line-length": null, + "media-feature-colon-space-after": "always", + "media-feature-colon-space-before": "never", + "media-feature-parentheses-space-inside": "never", + "media-feature-range-operator-space-after": "always", + "media-feature-range-operator-space-before": "always", + "media-query-list-comma-newline-after": "always-multi-line", + "media-query-list-comma-space-after": "always-single-line", + "media-query-list-comma-space-before": "never", + "no-empty-source": true, + "no-eol-whitespace": true, + "no-extra-semicolons": [true, { "severity": "warning" }], + "no-invalid-double-slash-comments": true, + "no-unknown-animations": true, + "property-case": "lower", + "property-no-unknown": true, + "selector-attribute-brackets-space-inside": "never", + "selector-attribute-operator-space-after": "never", + "selector-attribute-operator-space-before": "never", + "selector-combinator-space-after": "always", + "selector-combinator-space-before": "always", + "selector-list-comma-newline-after": "always", + "selector-list-comma-space-before": "never", + "selector-max-empty-lines": 0, + "selector-pseudo-class-case": "lower", + "selector-pseudo-class-no-unknown": true, + "selector-pseudo-class-parentheses-space-inside": "never", + "selector-pseudo-element-case": "lower", + "selector-pseudo-element-no-unknown": true, + "selector-type-case": "lower", + "selector-type-no-unknown": true, + "string-no-newline": true, + "time-min-milliseconds": 100, + "unit-disallowed-list": ["pt"], + "unit-case": "lower", + "unit-no-unknown": true, + "value-keyword-case": ["lower", {"ignoreKeywords": ["/(@|$)/"]}], + "value-list-comma-newline-after": "always-multi-line", + "value-list-comma-space-after": "always-single-line", + "value-list-comma-space-before": "never", + }, + "overrides": [ + { + "files": ["**/yui/**/*.css"], + "rules": { + } + } + ] +} \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..9ded974 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,26 @@ +module.exports = function(grunt) { + // Load the plugins + grunt.loadNpmTasks('grunt-stylelint'); + + // Project configuration + grunt.initConfig({ + stylelint: { + scss: { + options: { + quietDeprecationWarnings: true, + customSyntax: 'postcss-scss', + }, + src: ['snippets/**/*.scss'] + } + }, + watch: { + scss: { + files: ['snippets/**/*.scss'], + tasks: ['stylelint:scss'] + } + } + }); + + // Register default task + grunt.registerTask('default', ['stylelint:scss']); +}; \ No newline at end of file diff --git a/README.md b/README.md index 6b15cdf..d018b0e 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ Each particular snippet should have a `Scope` tag and thereby be assigned to a s - `global`: These snippets affect the entire site - `course`: These snippets affect only the course context - `dashboard`: These snippets affect only the dashboard of a user +- `sitehome`: These snippets affect only the site home page of site The list of scopes is not necessarily limited to these existing scopes, additional scopes can be added in the future. diff --git a/package.json b/package.json new file mode 100644 index 0000000..dff3cbb --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "moodle-theme_boost_union_snippets", + "version": "1.0.0", + "description": "SCSS snippets repository for Moodle Boost Union Theme", + "main": "index.js", + "scripts": { + "lint": "grunt stylelint:scss" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/moodle-an-hochschulen/moodle-theme_boost_union_snippets.git" + }, + "keywords": [], + "author": "", + "license": "GPL-3.0", + "bugs": { + "url": "https://github.com/moodle-an-hochschulen/moodle-theme_boost_union_snippets/issues" + }, + "homepage": "https://github.com/moodle-an-hochschulen/moodle-theme_boost_union_snippets#readme", + "devDependencies": { + "grunt": "^1.6.1", + "grunt-sass": "3.1.0", + "grunt-stylelint": "^0.19.0", + "postcss-scss": "^4.0.9", + "stylelint": "^15.11.0", + "stylelint-csstree-validator": "^3.0.0" + } +} diff --git a/snippets/easeofuse/course_search_above_sitehome_content.scss b/snippets/easeofuse/course_search_above_sitehome_content.scss new file mode 100644 index 0000000..f5bc39c --- /dev/null +++ b/snippets/easeofuse/course_search_above_sitehome_content.scss @@ -0,0 +1,36 @@ +/** + * Snippet Title: Move course search box to top on site home + * Scope: sitehome + * Goal: easeofuse + * Description: The course search box on site home is very important to many users. But when existing content on site home is very long, it is positioned quite unprominent. This snippet always puts the course search box on top on site home. + * Creator: André Menrath + * Tested on: Moodle 5.0, Firefox for Mac + * Usage note: For this snippet to have any effect, the course search box has to be enabled in the frontpageloggedin site admin setting. + * + * @copyright 2025 University of Graz + * @author André Menrath + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/* Make all site home content a flex container. This allows us to order the elements inside the container. */ +#page-site-index #region-main > div { + display: flex; + flex-direction: column; + width: 100%; + flex-wrap: nowrap; +} + +/* The course search box */ +#page-site-index #region-main > div > div.box:has(.simplesearchform) { + order: 1; +} + +/* The course content*/ +#page-site-index #region-main > div > div.course-content { + order: 2; +} + +/* All other stuff */ +#page-site-index #region-main > div > * { + order: 3; +} diff --git a/snippets/easeofuse/course_search_above_sitehome_content.webp b/snippets/easeofuse/course_search_above_sitehome_content.webp new file mode 100644 index 0000000..f8beadb Binary files /dev/null and b/snippets/easeofuse/course_search_above_sitehome_content.webp differ