{"id":23123028,"url":"https://github.com/devgeniem/tms-theme-base","last_synced_at":"2026-02-15T23:08:37.181Z","repository":{"id":36970549,"uuid":"359411635","full_name":"devgeniem/tms-theme-base","owner":"devgeniem","description":"Tampere WordPress Multisite Base Theme","archived":false,"fork":false,"pushed_at":"2026-02-03T19:16:46.000Z","size":6150,"stargazers_count":8,"open_issues_count":12,"forks_count":0,"subscribers_count":16,"default_branch":"master","last_synced_at":"2026-02-04T08:38:36.881Z","etag":null,"topics":["wordpress","wordpress-theme"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devgeniem.png","metadata":{"files":{"readme":"README.MD","changelog":"CHANGELOG.MD","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":".github/SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-04-19T10:02:34.000Z","updated_at":"2026-02-03T19:16:45.000Z","dependencies_parsed_at":"2023-10-16T18:52:31.700Z","dependency_job_id":"df488251-5227-46d0-9c56-7c543da502d3","html_url":"https://github.com/devgeniem/tms-theme-base","commit_stats":{"total_commits":1438,"total_committers":10,"mean_commits":143.8,"dds":0.6293463143254521,"last_synced_commit":"d27ecc89b75b51a0ebbe989eee09299527932991"},"previous_names":[],"tags_count":158,"template":false,"template_full_name":null,"purl":"pkg:github/devgeniem/tms-theme-base","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devgeniem%2Ftms-theme-base","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devgeniem%2Ftms-theme-base/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devgeniem%2Ftms-theme-base/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devgeniem%2Ftms-theme-base/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devgeniem","download_url":"https://codeload.github.com/devgeniem/tms-theme-base/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devgeniem%2Ftms-theme-base/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29491999,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T19:29:10.908Z","status":"ssl_error","status_checked_at":"2026-02-15T19:29:10.419Z","response_time":118,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["wordpress","wordpress-theme"],"created_at":"2024-12-17T07:32:05.381Z","updated_at":"2026-02-15T23:08:37.165Z","avatar_url":"https://github.com/devgeniem.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TMS Base Theme\n\n## Installation\n\nInstall the theme manually by cloning the repository under your WordPress themes directory\nand then removing the git tracking\n\n## Setup\n\nFirst you need to install required npm packages to start developing. In your theme root run:\n\n```\nnpm install\n```\n\n## Namespacing\n\nThe theme is namespaced as `\\TMS\\Theme\\Base`. WordPress does not support namespaces for template\nfiles and thus they do not adhere to the namespace.\n\n## Translations and textdomain\n\nTranslations and the theme textdomain are loaded from under the `/lang` directory.\nReplace the `tms-theme-base` string with your theme textdomain within all project files with a\ncase ***sensitive*** search and replace.\nThen rename the `.pot` file under the `/lang` directory with the new theme textdomain.\n\n## Polylang\n\nThe theme has built-in support for [Polylang][wp-polylang].\nIf Polylang has been installed and is active, a language switcher will automatically show up in the main nav.\n\n## Advanced Custom Fields\nIf [Advanced Custom Fields][acf] is installed and active,\ntheme settings will be available. Examples of things that can be changed in the theme settings are\nlogos and footer content.\n\nLanguage specific theme settings are available when both Advanced Custom Fields and Polylang.\n\n## Bulma and Bulmally\n\nThe theme has been designed to make use of the [Bulma][bulma] CSS Framework and\nthe [Bulmally][bulmally] accessibility-ready frontend component framework.\nRefer to that repository for modals, accordions, tabs and other dynamic components.\n\n\n## Theme info\n\n- Namespace: `\\TMS\\Theme\\Base`\n\nMake sure to change theme and author info in `style.css`.\n\n## Theme directory structure\n\nThe DustPress Theme consists of the following directories:\n\n- `/assets` - Scripts and styles.\n- `/lang` - Theme localization files.\n- `/lib` - Theme backend libraries following [PSR-4 Autoloader][psr4] specifications.\n- `/models` - DustPress models.\n- `/partials` - DustPress partials.\n- `/utils` - Utility scripts for theme development.\n\n## Partial directory structure\n\nThe partials in The DustPress Theme have been organized in the following way:\n- `/partials/` - The dust-files in the partials root directory are the main partials\n  for each model (page.dust, single.dust...)\n- `/partials/views/\u003cVIEW\u003e` - Contains partials that are used _only_ by a specific view.\n  Eg. `/partials/views/archive` contains dust-files that are used only by `archive.dust` (or its children).\n- `/partials/shared` - Common shared partials for views. Partials that are required only once per page.\n  Eg. `/partials/shared/header/header.dust`\n- `/partials/ui` - Common reusable ui-components that can be used multiple times in many places.\n  Eg. `/partials/ui/ratio-image` is used by archive-cards, and the featured image of a single article (post).\n\nFeel free to change the partial structure in any way you wish.\n\n## Theme development guide\n\n### Post types\n\nAll post types defined in the theme should be written in the `PostType` directory in a class\nimplementing the `TMS\\Theme\\Base\\Interfaces\\PostType` interface and registering itself in the wanted\nhook via `hooks()` method.\n\n#### Default classes\n\nThe theme comes with predefined classes for WordPress' default post types. Use these to handle data for them.\n\n- **TMS\\Theme\\Base\\PostType\\Post**: The class presentation of the default post type `post`.\n- **TMS\\Theme\\Base\\PostType\\Page**: The class presentation of the default post type `page`.\n- **TMS\\Theme\\Base\\PostType\\Attachment**: The class presentation of the default post type `attachment`.\n\n### Taxonomies\n\nAll taxonomies defined in the theme should be written in the `Taxonomy` directory in a class\nimplementing the `TMS\\Theme\\Base\\Interfaces\\Taxonomy` interface and registering itself in the wanted\nhook via `hooks()` method.\n\n### Users and roles\n\nPlease use our [devgeniem/wp-geniem-roles][wp-geniem-roles] package to modify Users and roles.\n\n### Advanced Custom Fields\n\nThe ACFController class handles the loading of the ACF fields from the `lib/ACF` directory.\nThe files in the directory are only required by the controller, so all the logic must be\nimplemented by the developer themself.\n\n### Templates and models\n\nThe theme contains a base model `BaseModel` that has a default submodel binding.\n\n### Logging\n\n`\\TMS\\Theme\\Base\\Logger` handles logging in [`syslog`](https://en.wikipedia.org/wiki/Syslog) compatible logging levels.\n\nThe logged messages are finally passed to `error_log()` function, which in part uses configured logging destination.\n\nTo control what is finally outputted to the log, you can change `GENIEM_LOG_LEVEL` environment variable.\nBelow is a quick table for level (used to control the logging level), class method, and description\nwhen to use in your code.\n\n| Level | Method        | Description                                          |\n|-------|---------------|------------------------------------------------------|\n| 100   | `debug()`     | Debug-level messages.                                |\n| 200   | `info()`      | Informational messages.                              |\n| 250   | `notice()`    | Normal but significant conditions.                   |\n| 300   | `warning()`   | When something's missing, or uses deprecated option. |\n| 400   | `error()`     | Something's gone wrong, but code can still continue. |\n| 500   | `critical()`  | Something has gone critically wrong.                 |\n| 550   | `alert()`     | A condition that should be corrected immediately.    |\n| 600   | `emergency()` | System is unusable. Crash and burn.                  |\n\nThe Easiest way to use the logger is like this:\n\n```php\ntry {\n    throw new Exception( 'This is an example.' );\n}\ncatch ( \\Exception $e ) {\n    ( new \\TMS\\Theme\\Base\\Logger() )-\u003eerror( $e-\u003egetMessage(), $e-\u003egetTrace() );\n}\n```\n\n##  Assets and Webpack\n\n[webpack][webpack-what-is] is used to compile the assets.\nUse [npm][npm] to install packages and require JavaScript files in `assets/scripts/main.js`\nand import SCSS files in `assets/styles/main.scss`.\nThe directory under which the assets are build is `assets/dist`.\n\nYou should only enqueue files that are under `assets/dist` in your theme's PHP code!\nTo use node modules, import them into your theme scripts.\n\nThe URL to be used with the assets has been defined in `/lib/Setup.php` with the `DPT_ASSET_URI` constant.\nIt points to `https://{site_domain}/{path_to_themes_folder}/themename/dist` by default.\nTo use another source for assets this value can be changed by defining the constant for example in\nthe `wp-config.php` file with a custom URI.\n\n## Asset versioning\n\nThe style and script files are automatically enqueued with the current theme version.\nTo bust browser cache on asset updates change the theme version in the `style.css` file comments.\n\n### Development\n\nRun webpack in the theme root in your local environment.\n\nRun with the npm script:\n\n```\nnpm run watch\n```\n\nThese commands will compile *unminified* versions of your assets.\n\n### Production\n\nBuild _minified_ versions for production with the npm script:\n\n```\nnpm run build\n```\n\nThis command will compile *minified* versions of your assets.\n\n### Testing with Browsersync\n\nThe project includes a [BrowserSync][browsersync] setup that will proxy your site to `localhost:3000`,\nand an IP address on your local network. This enables you to develop and test your project simultaneously with\nmultiple browsers and devices with a scroll and click synchronisation. The settings for Browsersync are in\n`webpack.config.js`.\n\nYou should change at least the `wpProjectUrl` constant to point to your local development server.\nBy default, Browsersync monitors CSS, JS, PHP and Dust files for changes and reloads all browsers automatically.\nFor more information on available settings etc, read the [webpack plugin documentation][npm-browsersync-webpack]\nand the [Browsersync documentation][browsersync-docs].\n\n## JavaScript development guide\n\nThe theme's Webpack config uses [Babel][babeljs] to compile [ES6][wikipedia-es6] into [ES5][wikipedia-es5].\nThus, we use [classes][es6-features-class] and other cool features introduced in ES6.\n[See the full list of ES6 features here][es6-features].\n\n### Enable Babel compiling\n\nIf you add *npm packages* using ES6 features, remember to include them for\nthe Babel loader in the `webpack.config.js` file!\n\n```javascript\n// List paths to packages using ES6 to enable Babel compiling.\n{\n    include: [\n        path.resolve( __dirname, 'assets/scripts' ),\n        path.resolve( __dirname, 'node_modules/foundation-sites' )\n    ]\n}\n```\n\n*TerserJS will most likely produce an error when trying to minify an ES6 script that\nis not included for the Babel loader while running `npm run build`!*\n\n### Theme scripts\n\nThe theme's main JS file `theme.js` holds the main theme class.\nThe `Theme` class runs other theme JS classes automatically on the document ready event.\nTo enable autoloading on JS side define your template scripts as follows:\n\n1. Create a separate script file for each WordPress template, for example `page-frontpage.js`.\n    You can also create script files that are loaded anyway regardless of the template.\n\n2. Define a JS class mimicking your DustPress model and export the class reference,\n    for example `export default class PageFrontpage {}`.\n\n3. The document's html element will automatically get a class from your main model,\n    for example `\u003chtml class=\"PageFrontpage\"\u003e`.\n    If you add more scripts for a specific template, add the corresponding class names into\n    the `html` element's classlist with `dustpress/document_class` filter.\n\n4. Import your scripts in `theme.js` and add them into the appropriate controller lists.\n    The order of the scripts is essential if you need to access methods from other script classes.\n\n   ```javascript\n   // First import the scripts you want to use\n   import Common from './common';\n   import PageFrontpage from './page-frontpage';\n\n   // Add your global scripts here.\n   const globalControllers = {\n       Common\n   };\n\n   // Add your template-specific scripts here.\n   const templateControllers = {\n       PageFrontpage\n   };\n   ```\n\n5. The `Theme` class will then automatically run a method called `docReady` when the document is ready.\n*Remember to define it in your template class!*\n\n### The `Theme` class\n\nThe `Theme` class controls the theme script classes. The class instance is accessible globally and\nthus `Theme` is a reserved JavaScript object name in our themes. `Theme` holds all scripts under\ncorresponding class properties.\n\nGlobal scripts are under `_globalControllers` and template specific scripts are under `_templateControllers`.\nThese properties are hash maps meaning each controller is under a key defined with the class name:\n\n```javascript\nclass Theme {\n  constructor() {\n      // [...]\n      this._templateController = {\n        className: classInstance\n      }\n      // [...]\n  }\n}\n```\n\nThe controller classes are instantiated by the set methods (`setGlobalControllers` and `setTemplateControllers`)\nrun by the `main.js`. Thus, an instance of a class is created and accessible before the document is ready and loaded.\nTemplate controllers are instantiated only if the corresponding style class name is defined for the `html` element\nin the DOM. If the template controller class is not instantiated, you can still access it statically by\ncalling `Theme.{ClassName}`.\n\n**This class is not to be modified! Use other script files to do the magic in you theme!**\n\n#### Accessing controllers\n\nTo access a class instance for example in some inline script, fetch it as follows:\n\n```javascript\nvar frontpageInstance = Theme.getController(\"PageFrontpage\");\n```\n\nTo access a class reference, fetch it as follows:\n\n```javascript\nvar commonClass = Theme.Common;\n```\n\nClass references are useful if you need global static methods. For instance, you might create a\nstatic global Vanilla JS query selector under `common.js`:\n\n```javascript\nexport default class Common {\n    // Select a list of matching elements, context is optional.\n    static $(selector, context = undefined) {\n        return (context || document).querySelectorAll(selector);\n    }\n}\n```\n\n...and then use it as follows:\n\n```javascript\nlet element = Theme.Common.$('.my-element');\n```\n\n#### `docReady` methods\n\nIf you manipulate the DOM dynamically and need to rerun *all* `docReady` methods for the current\ntemplate *(controlled by the current html element classlist)*, run it with the class method:\n\n```javascript\nTheme.runDocReady();\n```\n\nAlternatively you can run the `docReady` for a single controller instance:\n\n```javascript\nTheme.getController('PageFrontpage').docReady();\n```\n\n#### `init` methods\n\nThe `Theme` class runs a method called `init` for all the global scripts, and the currently defined template\nscripts after all scripts are instantiated. If you want access other script classes on the class constructor,\nthey might not be accessible yet due to the `require` order. By running your scripts on the `init` method all\nother script classes are already loaded and instantiated. Here is an example usage:\n\n```javascript\nclass PageFrontpage {\n    init() {\n      // I need to acces the sidebar!\n      this.Sidebar = Theme.getController('Sidebar');\n      // Now we can run stuff before the docReady..\n      this.Sidebar.frontPageMakesMeDoStuff();\n    }\n}\n```\n\n### The `Common` class\n\nThe theme assets include a default common script class called `Common`. Use this as the default class for all of your\n global methods and properties. For instance, you might handle the main menu in the `Common` class.\n\n*Do not bloat this class! Do not be afraid to introduce more global controller classes\nif your script files get too long! What ever you do, [D.R.Y][wikipedia-dry]!*\n\n#### Useful methods under `Common`\n\n#### **`stop`**\n\nThis **static** method enables you to safely stop the default event on event listener callbacks. Use it as follows:\n\n```javascript\nfunction myEventCallback(e) {\n  Theme.Common.stop(e);\n  // Do some other stuff with the event..\n}\n```\n\n#### `**$**`\n\nThis **static** method wraps the `querySelectorAll` function and lets you select a list of matching elements.\nTo optimize the query add a context element and only query under it.\n\n```javascript\nlet elements = Theme.Common.$('.my-element', myParentElementObject);\n```\n\n#### **`$1`**\n\nThis **static** method wraps the `querySelector` functions to select a single element from the `DOM`.\nTo optimize the query add a context element and only query under it.\n\n```javascript\nlet element = $1('.my-element', myParentElementObject);\n```\n\n### Global event listener and data-cmd attributes\n\nTheme class adds global click event listener which you can use by adding data-cmd and data-ctrl attributes\nto your html element, where `data-ctrl` defines the JavaScript controller class and `data-cmd` the method to be called.\n```\n\u003cbutton data-cmd=\"doSomething\" data-ctrl=\"MyAwesomeController\" /\u003e\n```\n\nThe method receives two parameters. The first parameter is the event, and the second is the actual\nelement that has the `data-cmd` and `data-ctrl` attributes.\n\nSo in the Class `MyAwesomeController` we could have a method like this:\n\n```javascript\nclass MyAwesomeController {\n    /**\n     * Set aria-expanded to true when element is clicked.\n     *\n     * @param {object|Event}              e  The button click event.\n     * @param {object|HTMLElement|jQuery} el The dom object of the element that was clicked.\n     * @return {void}\n     */\n    doSomething(e, el) {\n        el.setAttribute('aria-expanded', 'true');\n    }\n}\n```\n\n### External libraries\n\n#### jQuery\n\nAll scripts rely on WordPress enqueueing jQuery as an external library.\nWordPress exposes jQuery as a public library under the global context.\nTo use the `$` shorthand, add following to the beginning of your script file:\n\n```javascript\nconst $ = jQuery;\n```\n\n#### Lodash\n\n[Lodash][lodash] is a modern JavaScript utility library delivering modularity, performance \u0026 extras.\nSee the [documentation][lodash-docs] for usage guide.\nTo import Lodash, add the following to the beginning of your script file:\n\n```javascript\nimport _ from 'lodash';\n```\n\n##  Contributors\n\n- [Our contributors](https://github.com/devgeniem/tms-theme-base/graphs/contributors)\n\n[acf]: https://www.advancedcustomfields.com/\n[babeljs]: https://babeljs.io/\n[browsersync-docs]: https://browsersync.io/docs\n[browsersync]: https://browsersync.io\n[bulma]: https://bulma.io/\n[bulmally]: https://devgeniem.github.io/bulmally/\n[dustpress-github]: https://github.com/devgeniem/dustpress/\n[es6-features-class]: http://es6-features.org/#ClassDefinition\n[es6-features]: http://es6-features.org\n[lodash-docs]: https://lodash.com/docs/4.17.10\n[lodash]: https://lodash.com/\n[npm-browsersync-webpack]: https://www.npmjs.com/package/browser-sync-webpack-plugin\n[npm]: https://www.npmjs.com/\n[psr4]: https://www.php-fig.org/psr/psr-4/\n[webpack-what-is]: https://webpack.github.io/docs/what-is-webpack.html\n[wikipedia-dry]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself\n[wikipedia-es5]: https://en.wikipedia.org/wiki/ECMAScript#5th_Edition\n[wikipedia-es6]: https://en.wikipedia.org/wiki/ECMAScript#6th_Edition_-_ECMAScript_2015\n[wp-geniem-roles]: https://github.com/devgeniem/wp-geniem-roles\n[wp-polylang]: https://wordpress.org/plugins/polylang/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevgeniem%2Ftms-theme-base","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevgeniem%2Ftms-theme-base","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevgeniem%2Ftms-theme-base/lists"}