Planet Drupal
Liip: DrupalCon Barcelona Recap
DrupalCon Barcelona Group picture by Bram Driesen (source)
Hola de nou barcelonaBarcelona for me has a long history of Drupal. For Drupal Dev Days 2012 I organized a mapping sprint, we cycled to DrupalCon Barcelona 2015 as part of the #tourdedrupal report , pictures and it was nice to visit a more local event - Drupal Summer - in 2016 (report, pictures).
Coding beyond functionality. Altering technologies through artistic research by Mónica Rikić Drupal CMS: una nova experiència immediata per a DrupalThis year, the focus was on Drupal CMS (previously known by its internal development name Starshot) which is all about bringing Drupal to the next level by creating an out-of-the-box experience including a new way to create layouts (Experience Builder), integrating AI features and many more features. If you like to dive deeper into Drupal CMS, check out the meta issue listing all work tracks as well as the landing page on Drupal.org.
Driesnote by Dries BuytaertA similar, prepackaged version of Drupal already exists, handcrafted by many individuals and agencies. Recipes have recently been introduced to Drupal core. They allow to package configuration and content to pre-configure Drupal for common use cases like Search, SEO-optimization or an Event calendar feature.
Check out our blökkli starterkit that provides you will a fully-preconfigured setup that we use at Liip.
DrupalCon Crowd Drupal al governWe saw Drupal's wide adoption for Government at DrupalCon Barcelona.
Implementing AI solutions for the French government - in this session it was demonstrated how public services were improved. AI technology would support the public servants by pregenerating responses that would be validated and modified by the public servant. The use of AI technology cut down response times from an average of 19 days to 3 days and a plus of 11% found the received answers helpful.
Running a fleet of web sites with ease via LocalGov Drupal Microsites Platform showed a feature that we are also planning to roll out to one of our customers soon. You can easily manage multiple microsites in Drupal and configure on a per-site-basis the needed styling options in order to customize it to each site needs. By leveraging one CMS basis, the time-to-market and total cost of ownership for each microsite can be heavily reduced.
Jonathan Noack & Thom Nagy presenting about bs.chLarge-scale content creation with Drupal — Delights, Pitfalls and support structures to help editors - in this session our customer Thom Nagy & our product owner Jonathan Noack presented their case study on relaunching bs.ch. I liked to see how a trustful collaboration with strong communication and stakeholder management combined with agile delivery and innovation led to an outcome all participants are proud about.
The website is our flagship showcase for blökkli, the interactive pagebuilder that has delighted many of our customers already and is available for the community under the open-source license.
Even though the canton is embedded in a typically restrictive government environment, they even launched the first AI-based assistant "Alva" that answers any questions the public might have about the canton in their own language using GPT-based technology.
SostenibilitatConferences that bring together folks from all over the world have a hard time being sustainable. I appreciate the efforts to promote sustainable transport for example when amongst the attendees traveling by train, a winner was selected.
The organizing team also worked with the sponsors to make sure they were taking sustainability into account for how they set up their booths and limit the swag they would give out to participants.
DrupalCon Barcelona Mascot watching the crowdThe next DrupalCon Europe has been announced to happen in Vienna October 14-17 2025. I am particularly excited for this location as I grew up in Vienna and have been part of the Drupal Austria community as an organizing member before moving to Switzerland.
As Vienna is at the heart of Europe, I encourage you to think about sustainable ways to get to the conference.
If you travel via Hamburg, Bregenz/Feldkirch, Roma/Blorence/Bologna or Amsterdam, you should even be able to sleep on one of the new generation nightrains that offers better comfort, single-cabins and wheelchair-accessible sleepers. You typically can book your train 6 months ahead. As they introduced dynamic pricing recently, it is recommended to book your tickets early.
The beach alongside DrupalCon BarcelonaI leave you with some pictures from this year in Barcelona.
Would you like to learn more about Drupal? Find an event near you. See you 2025 at one of my favorites Drupal Mountain Camp in Davos or at DrupalCon Vienna.
Web Wash: How to Organize Content Entity Forms in Drupal
Drupal provides a robust framework for creating and managing content. However, managing form fields in complex content entities can be challenging. The Field Group, Inline Entity Form, and Conditional Fields modules offer practical tools for improving the user experience and streamlining the content creation process.
In the above live stream, we’ll use all three modules to organize and clean up fields on an “Event” content type.
Below are the show notes for the live stream.
#! code: Drupal 11: Using The Batch API To Process CSV Files
This is the fourth article in a series of articles about the Batch API in Drupal. The Batch API is a system in Drupal that allows data to be processed in small chunks in order to prevent timeout errors or memory problems.
So far in this series we have looked at creating a batch process using a form, followed by creating a batch class so that batches can be run through Drush and then using the finished state to control batch processing. All of these articles go together to form a basis of batch processing in Drupal.
In this article we will look at bringing these concepts together to perform a task that is quite common on websites, processing Comma Separated Value or CSV files, which we will do using the Drupal Batch API.
Processing CSV data is very common on the web. Whilst it is quite common to integrate with an API it is sometimes simpler to just generate a CSV file of the data you need from one system and upload it into a form on a website. Most systems will allow you to export data as a CSV file of some kind, but it's also very easy to export a CSV from commonly used programs like Excel and Google Sheets.
Processing a CSV file in PHP is quite simple, but once you reach one hundred records you will find that PHP will start to throw errors due to timeouts or memory issues. The solution is to employ the Batch API to spread the load of that processing over a number of different requests.
Drupal life hack's: Drupal Link Generation: Comparing link_generator, renderer, and Url Methods
Mario Hernandez: Automating your Drupal Front-end with ViteJS
Modern web development relies heavily on automation to stay productive, validate code, and perform repetitive tasks that could slow developers down. Front-end development in particular has evolved, and it can be a daunting task to configure effective automation. In this post, I'll try to walk you through basic automation for your Drupal theme, which uses Storybook as its design system.
Recently I worked on a large Drupal project that needed to migrate its design system from Patternlab to Storybook. I knew switching design systems also meant switching front-end build tools. The obvious choice seemed to be Webpack, but as I looked deeper into build tools, I discovered ViteJS.
Vite is considered the Next Generation Frontend Tooling, and when tested, we were extremely impressed not only with how fast Vite is, but also with its plugin's ecosystem and its community support. Vite is relatively new, but it is solid and very well maintained. Learn more about Vite.
The topics covered in this post can be broken down in two categories:
-
Preparing the Front-end environment
-
Automating the environment
In a previous post, I wrote in detail how to build a front-end environment with Vite and Storybook, I am going to spare you those details here but you can reference them from the original post.
- In your command line, navigate to the directory where you wish to build your environment. If you're building a new Drupal theme, navigate to your site's web/themes/custom/
- Run the following commands (Storybook should launch at the end):
Vite and Storybook ship with a handful of useful scripts. We may find some of them already do what we want or may only need minor tweaks to make them our own.
- In your code editor, open package.json from the root of your newly built project.
- Look in the scripts section and you should see something like this:
To run any of those scripts, prefix them with npm run. For example: npm run build, npm run lint, etc. Let's review the scripts.
- dev: This is a Vite-specific command which runs the Vite app we just build for local development
- build: This is the "do it all" command. Running npm run build on a project runs every task defined in the build configuration we will do later. CI/CD runners run this command to build your app for production.
- lint: Will lint your JavaScript code inside .js or .jsx files.
- preview: This is also another Vite-specific command which runs your app in preview mode.
- storybook: This is the command you run to launch and keep Storybook running while you code.
- build-storybook: To build a static version of Storybook to package it or share it, or to run it as a static version of your project.
In front-end development, it is important everyone in your team use the same version of NodeJS while working in the same project. This ensures consistency in your project's behavior for everyone in your team. Differences in the node version your team uses can lead to inconsistencies when the project is built. One way to ensure your team is using the same node version when working in the same project, is by adding a .nvmrc file in the root of your project. This file specifies the node version your project uses. The node version is unique to each project, which means different projects can use different node versions.
- In the root of your theme, create a file called .nvmrc (mind the dot)
- Inside .nvmrc add the following: v20.14.0
- Stop Storybook by pressing Ctrl + C in your keyboard
- Build the app:
You only need to run nvm install once per project unless the node version changes. If you switch to a project that uses a different node version, when you return to this project, run nvm use to set your environment back to the right node version.
The output in the command line should look like this:
By default, Vite names the compiled files by appending a random 8-character string to the original file name. This works fine for Vite apps, but for Drupal, the libraries we'll create expect for CSS and JS file names to stay consistent and not change. Let's change this default behavior.
- First, install the glob extension. We'll use this shortly to import multiple CSS files with a single import statement.
- Then, open vite.config.js in your code editor. This is Vite's main configuration file.
- Add these two imports around line 3 or directly after the last import in the file
- Still in vite.config.js, replace the export default... with the following snippet which adds new settings for file names:
- First we imported path and { glob }. path is part of Vite and glob was added by the extension we installed earlier.
- Then we added a build configuration object in which we defined several settings:
- emptyOutDir: When the build job runs, the dist directory will be emptied before the new compiled code is added.
- outDir: Defines the App's output directory.
- rollupOptions: This is Vite's system for bundling code and within it we can include neat configurations:
- input: The directory where we want Vite to look for CSS and JS files. Here's where the path and glob imports we added earlier are being used. By using src/**/**/*.{css,js}, we are instructing Vite to look three levels deep into the src directory and find any file that ends with .css or .js.
- output: The destination for where CSS and JS will be compiled into (dist/css and dist/js), respectively. And by setting assetFileNames: 'css/[name].css', and entryFileNames: 'css/[name].js', CSS and JS files will retain their original names.
Now if we run npm run build again, the output should be like this:
The random 8-character string is gone and notice that this time the build command is pulling more CSS files. Since we configured the input to go three levels deep, the src/stories directory was included as part of the input path.
2. Restructure the projectThe out of the box Vite project structure is a good start for us. However, we need to make some adjustments so we can adopt the Atomic Design methodology. This is today's standards and will work well with our Component-driven Development workflow. At a high level, this is the current project structure:
> .storybook/ > dist/ > public/ > src/ |- stories/ package.json vite.config.js- > .storybook is the main location for Storybook's configuration.
- > dist is where all compiled code is copied into and where the production app looks for all code.
- > public is where we can store images and other static assets we need to reference from our site. Equivalent to Drupal's /sites/default/files/.
- > src is the directory we work out of. We will update the structure of this directory next.
- package.json tracks all the different node packages we install for our app as well as the scripts we can run in our app.
- vite.config.js is Vite's main configuration file. This is probably where we will spend most of our time.
The Atomic Design methodology was first introduced by Brad Frost a little over ten years ago. Since then it has become the standard for building web projects. Our environment needs updating to reflect the structure expected by this methodology.
- First stop Storybook from running by pressing Ctrl + C in your keyboard.
- Next, inside src, create these directories: base, components, and utilities.
- Inside components, create these directories: 01-atoms, 02-molecules, 03-organisms, 04-layouts, and 05-pages.
- While we're at it, delete the stories directory inside src, since we won't be using it.
Since the project structure has changed, we need to make Storybook aware of these changes:
- Open .storybook/main.js in your code editor
- Update the stories: [] array as follows:
The Stories array above is where we tell Storybook where to find our stories and stories docs, if any. In Storybook, stories are the components and their variations.
Add pre-built componentsAs our environment grows, we will add components inside the new directories, but for the purpose of testing our environment's automation, I have created demo components.
- Download demo components (button, title, card), from src/components/, and save them all in their content part directories in your project.
- Feel free to add any other components you may have built yourself. We'll come back to the components shortly.
Before we can see the newly added components, we need to configure Storybook to understands the Twig and YML code we are about to introduce within the demo components. To do this we need to install several node packages.
- In your command line run:
- Next, update vite.config.js with the following configuration. Add the snippet below at around line 5:
The configuration above is critical for Storybook to understand the code in our components:
- vite-plugin-twig-drupal, is the main TwigJS extension for our project.
- Added two new imports which are used by Storybook to understand Twig:
- vite-plugin-twig-drupal handles transforming Twig files into JavaScript functions.
- @modyfi/vite-plugin-yaml let's us pass data and variables through YML to our Twig components.
- Still in vite.config.js, add the twig and yml() plugins to add Twig namespaces for Storybook.
Since we removed the react() function by using the snippet above, we can remove import react from '@vitejs/plugin-react' from the imports list as is no longer needed.
With all the configuration updates we just made, we need to rebuild the project for all the changes to take effect. Run the following commands:
npm run build npm run storybookThe components are available but as you can see, they are not styled even though each component contains a CSS stylesheet in its directory. The reason is Storybook has not been configured to find the component's CSS. We'll address this shortly.
4. Configure postCSSWhat is PostCSS? It is a JavaScript tool or transpiler that turns a special PostCSS plugin syntax into Vanilla CSS.
As we start interacting with CSS, we need to install several node packages to enable functionality we would not have otherwise. Native CSS has come a long way to the point that I no longer use Sass as a CSS preprocessor.
- Stop Storybook by pressing Ctrl + C in your keyboard
- In your command line run this command:
- At the root of your theme, create a new file called postcss.config.js, and in it, add the following:
One cool thing about Vite is that it comes with postCSS functionality built in. The only requirement is that you have a postcss.config.js file in the project's root. Notice how we are not doing much configuration for those plugins except for defining them. Let's review the code above:
- postcss-import the base for importing CSS stylesheets.
- postcss-import-ext-glob to do bulk @import of all CSS content in a directory.
- postcss-nested to unwrap nested rules to make its syntax closer to Sass.
- postcss-preset-env defines the CSS browser support level we need. Stage 4 means we want the "web standards" level of support.
The goal here is to ensure that every time a new CSS stylesheet or JS file is added to the project, Storybook will automatically be aware and begin consuming their code.
NOTE: This workflow is only for Storybook. In Drupal we will use Drupal libraries in which we will include any CSS and JS required for each component.There are two types of styles to be configured in most project, global styles which apply site-wide, and components styles which are unique to each component added to the project.
Global styles- Inside src/base, add two stylesheets: reset.css and base.css.
- Copy and paste the styles for reset.css and base.css.
- Inside src/utilities create utilities.css and in it paste these styles.
- Inside src/, create a new stylesheet called styles.css.
- Inside styles.css, add the following imports:
The order in which we have imported our stylesheets is important as the cascading order in which they load makes a difference. We start from reset to base, to utilities.
- reset.css: A reset stylesheet (or CSS reset) is a collection of CSS rules used to clear the browser's default formatting of HTML elements, removing potential inconsistencies between different browsers before any of our styles are applied.
- base.css: CSS Base applies a style foundation for HTML elements that is consistent for baseline styles such as typography, branding and colors, font-sizes, etc.
- utilities.css: Are a collection of pre-defined CSS rules we can apply to any HTML element. Rules such as variables for colors, font size, font color, as well as margin, sizes, z-index, animations, etc.
Before our components can be styled with their unique and individual styles, we need to make sure all our global styles are loaded so the components can inherit all the base/global styles.
- Inside src/components create a new stylesheet, components.css. This is where we are going to gather all components styles.
- Inside components.css add glob imports for each of the component's categories:
There are several ways in which we can make Storybook aware of our styles and javascript. We could import each component's stylesheet and javascript into each *.stories.js file, but this could result in some components with multiple sub-components having several CSS and JS imports. In addition, this is not an automated system which means we need to manually do imports as they become available. The approach we are going to take is importing the stylesheets we created above into Storybook's preview system. This provides a couple of advantages:
- The component's *.stories.js files are clean without any css imports as all CSS will already be available to Storybook.
- As we add new components with individual stylesheets, these stylesheets will automatically be recognized by Storybook.
Remember, the order in which we import the styles makes a difference. We want all global and base styles to be imported first, before we import component styles.
- In .storybook/preview.js add these imports at the top of the page around line 2.
In addition to importing two new extensions: twig and twig-drupal-filters, we setup a setupFilters function for Storybook to read Drupal filters we may use in our components. We are also importing two of the stylesheets we created earlier:
- styles.css contains all the CSS code from reset.css, base.css, and utilities.css (in that order)
- components.css contains all the CSS from all components. As new components are added and they have their own stylesheets, they will automatically be included in this import.
On a typical project, you will find that the majority of your components don't use JavaScript, and for this reason, we don't need such an elaborate system for JS code. Importing the JS files in the component's *.stories.js should work just fine. Since the demo components dont use JS, I have commented near the top of card.stories.js how the component's JS file would be imported if JS was needed.
If the need for a more automated JavaScript processing workflow arose, we could easily repeat the same CSS workflow but for JS.
Build the project againNow that our system for CSS and JS is in place, let's build the project to ensure everything is working as we expect it.
npm run build npm run storybookYou may notice that now the components in Storybook look styled. This tells us our new system is working as expected. However, the Card component, if you used the demo components, is missing an image. We will address this issue in the next section.
This concludes the preparation part of this post. The remaining part will focus on creating automation tasks for compiling, minifying and linting code, copying static assets such as images, and finally, watching for code changes as we code. 6. Copying images and other assetsCopying static assets like images, icons, JS, and other files from src into dist is a common practice in front-end projects. Vite comes with built-in functionality to do this. Your assets need to be placed in the public directory and Vite will automatically copy them on build. However, sometimes we may have those assets alongside our components or other directories within our project.
In Vite, there are many ways to accomplish any task, in this case, we will be using a nice plugin called vite-plugin-static-copy. Let's set it up.
- If Storybook is running, kill it with Ctrl + C in your keyboard
- Next, install the extension by running:
- Next, right after all the existing imports in vite.config.js, import one more extension:
- Lastly, still in vite.config.js, add the viteStaticCopy function configuration inside the plugins:[] array:
The viteStaticCopy function we added allows us to copy any type of static assets anywhere within your project. We added a target array in which we included src and dest for the images we want copied. Every time we run npm run build, any images inside any of the components, will be copied into dist/images.
If you need to copy other static assets, simply create new targets for each.
- Build the project again:
The missing image for the Card component should now be visible, see below. Pretty sweet! 🍰
7. The Watch taskA watch task makes it possible for developers to see the changes they are making as they code, and without being interrupted by running commands. Depending on your configuration, a watch task watches for any changes you make to CSS, JavaScript and other file types, and upon saving those changes, code is automatically compiled, and a Hard Module Reload (HMR) is evoked, making the changes visible in Storybook.
Although there are extensions to create watch tasks, we will stick with Storybook's out of the box watch functionality because it does everything we need. In fact, I have used this very approach on a project that supports over one hundred sites.
I actually learned this the hard way, I originally was importing the key stylesheets in .storybook/preview.js using the files from dist. This works to an extend because the code is compiled upon changes, but Storybook is not aware of the changes unless we restart Storybook. I spent hours debugging this issue and tried so many other options, but at the end, the simple solution was to import CSS and JS into Storybook's preview using the source files. For example, if you look in .storybook/preview.js, you will see we are importing two CSS files which contain all of the CSS code our project needs:
import '../src/styles.css'; import '../src/components/components.css';Importing source CSS or JS files into Storybook's preview allows Storybook to become aware immediately of any code changes.
The same, or kind of the same works for JavaScript. However, the difference is that for JS, we import the JS file in the component's *.stories.js, which in turn has the same effect as what we've done above for CSS. The reason for this is that typically not every component we build needs JS.
A real watch taskCurrently we are running npm run storybook as a watch task. Nothing wrong with this. However, to keep up with standards and best practices, we could rename the storybook command, watch, so we can run npm run watch. Something to consider.
You could also make a copy of the storybook command and name it watch and add additional commands you wish to run with watch, while leaving the original storybook command intact. Choices, choices.
8. Linting CSS and JavaScriptOur workflow is coming along nicely. There are many other things we can do but for now, we will end with one last task: CSS and JS linting.
- Install the required packages. There are several of them.
- Next, after the last import in vite.config.js, add one more:
- Then, let's add one more plugin in the plugins:[] array:
So we can execute the above checks on demand, we can add them as commands to our app.
- In package.json, within the scripts section, add the following commands:
- We installed a series of packages related to ESLint and Stylelint.
- vite-plugin-checker is a plugin that can run TypeScript, VLS, vue-tsc, ESLint, and Stylelint in worker thread.
- We imported vite-plugin-checker and created a new plugin with two checks, one for ESLint and the other for Stylelint.
- By default, the new checks will run when we execute npm run build, but we also added them as individual commands so we can run them on demand.
Both ESLint and Stylelint use configuration files where we can configure the various rules we want to enforce when writing code. The files they use are eslint.config.js and .stylelintrc.yml respectively. For the purpose of this post, we are only going to add the .stylelintrc.yml in which we have defined basic CSS linting rules.
- In the root of your theme, create a new file called .stylelintrc.yml (mind the dot)
- Inside .stylelintrc.yml, add the following code:
The CSS rules above are only a starting point, but should be able to check for the most common CSS errors.
Test the rules we've defined by running either npm run build or npm run stylelint. Either command will alert you of a couple of errors our current code contains. This tells us the linting process is working as expected. You could test JS linting by creating a dummy JS file inside a component and writing bad JS in it.
9. One last thingIt goes without saying that we need to add storybook.info.yml and storybook.libraries.yml files for this to be a true Drupal theme. In addition, we need to create the templates directory somewhere within our theme.
storybook.info.ymlThe same way we did for Storybook, we need to create namespaces for Drupal. This requires the Components module and storybook.info.yml configuration is like this:
components: namespaces: atoms: - src/components/01-atoms molecules: - src/components/02-molecules organisms: - src/components/03-organisms layouts: - src/components/04-layouts pages: - src/components/05-pages templates: - src/templates storybook.libraries.ymlThe recommended method for adding CSS and JS to components or a theme in Drupal is by using Drupal libraries. In our project we would create a library for each component in which we will include any CSS or JS the component needs. In addition, we need to create a global library which includes all the global and utilities styles. Here are examples of libraries we can add in storybook.libraries.yml.
global: version: VERSION css: base: dist/css/reset.css: {} dist/css/base.css: {} dist/css/utilities.css: {} button: css: component: dist/css/button.css: {} card: css: component: dist/css/card.css: {} title: css: component: dist/css/title.css: {} /templatesDrupal's templates' directory can be created anywhere within the theme. I typically like to create it inside the src directory. Go ahead and create it now.
- Inside storybook.info.yml, add a new Twig namespace for the templates directory. See example above. Update your path accordingly based on where you created your templates directory.
P.S: When the Vite project was originally created at the begining of the post, Vite created files such as App.css, App.js, main.js, and index.html. All these files are in the root of the project and can be deleted. It won't affect any of the work we've done, but Vite will no longer run on its own, which we don't need it to anyway.
In closingI realize this is a very long post, but there is really no way around it when covering these many topics in a single post. I hope you found the content useful and can apply it to your next Drupal project. There are different ways to do what I've covered in this post, and I challenge you to find better and more efficient ways. For now, thanks for visiting.
Download the themeA full version of the Drupal theme built with this post can be downloaded.
Make sure you are using the theme branch from the repo.
Drupal Starshot blog: Adopt a Document - new fundraising program to bring Drupal documentation to the next level
Every great product needs a great supporting documentation - this rule is as simple and well known as it is hard to stick to, especially if we are talking about the continuously growing Open Source system that Drupal is. A while ago we came to realise that Drupal documentation needs a revamp. Together with ever amazing community members, the DA engineering team has been looking for a solution that will bring world class documentation to Drupal and that joint effort did not go in vain as such a solution has been defined!
As each of you can imagine, the goal of overhauling documentation tooling to a modern docs-as-code system is a big challenge so it is logical that we decided to define bite-sized deliverables and today we are happy to announce the first phase of the project - delivering clear and easy to follow user guide for Drupal CMS. And we need your help!
The Drupal Association is kicking off an initiative to bring Drupal.org documentation and accompanying software to world-class level. For the first phase dedicated to the Drupal CMS we are partnering with Drupalize.me: they will create the gold-standard user guide for Drupal CMS.
But that is just the beginning! As a next step Drupal Association will onboard the Documentation Lead who, in close collaboration with the community, will help to ensure that Drupal.org documentation is clear, comprehensive and current.
Last but not the least, we will implement a docs-as-code system that will improve the creation and maintenance process of documentation.
Now the question is how you can help? And the answer is simple - by “Adopting a Document”!
Inspired by the “Adopt a Highway” program that is becoming more and more popular in North America, we created a way for the partners to get involved in creating great documentation by taking care of one of the Drupal CMS milestones and sponsoring creation and further maintenance of the documentation for one or multiple Drupal CMS working tracks.
Thanks to your support, future users of Drupal CMS will be able to easily find answers to the questions that might arise as they discover Drupal CMS capabilities.
But not only that, as we find it fair for the partners who will decide to contribute to the initiative to receive some benefits:
-
Your logo will be placed in the sidebar of the adopted documentation section—which Your logo will be placed on the documentation page(s) you adopt, visible to the thousands of new users we expect to try Drupal CMS
-
Your logo will be highlighted on Drupal.org as a Drupal CMS sponsor, as well as at DrupalCons for the next year!
-
For each $100 of contribution you will receive 1 credit that you will be able to use within 1 year.
We’ve got 30 documentation sections available for sponsorship so far. You can adopt one of those and by donating $2400 you will ensure that future users will be able to easily find answers to the questions that might arise as they discover Drupal CMS as well as will enable for the next phase of the project to get started.
Sounds exciting, right? So if you are keen to be part of Adopt a document - do reach out to me and let’s work together to bring Drupal Documentation to the new level!
The Drop Times: The la_eu Site Project Takes a Step Further at Barcelona
The Drop Times: DrupalCon Barcelona Wrap-Up: The Third and Final Day
Drupal Association blog: DrupalCon Barcelona 2024 brings the open source Drupal community together
DrupalCon, the main event about the digital experience platform Drupal, was held this year in Barcelona, Spain, from 24-27 September. DrupalCon unites thousands of people from around the globe who use, develop, design, and support the Drupal platform. Over 1,300 digital experts and Drupal professionals gathered to exchange ideas, work on the Drupal project, and propel Drupal innovation.
On 24 September, Founder and Project Lead Dries Buytaert gave an inspiring keynote about developing an exciting new product - Drupal CMS. Dries shared a progress update on the timeline of Drupal CMS, showing a demo of the current state, the event-building and SEO capabilities, and the acceleration of site building through AI tools. This will allow Drupal experts and potential end users to drastically change how they build websites, using prompts to create in minutes what used to take days.
A three-day program of over 100 sessionsDrupalCon Barcelona hosted over 100 workshops, presentations, and several exciting keynotes in a packed three-day program. After the Opening Ceremony, the Women in Drupal Awards were held, recognizing the incredible impact Drupal has on Diversity, Equity, and Inclusion. Winners Pamela Barone, All Petrovska, and Esmeralda Tijhoff took home the awards, acknowledging their accomplishments in the Drupal community.
For almost ten years, the results of the Drupal Business Survey have been presented at DrupalCon. The Drupal Business Survey, organized by the Drupal Business Network, has been collecting data from Drupal service providers and agencies from all over the world. On 26 September, insights from the 2024 survey were shared in an informative session. While the digital industry is not unaware of worldly events that impact economy and sales, many agencies are excited about Drupal CMS. Over 70 digital agencies working with Drupal came to gather to review and work on their strategy for 2025 and beyond.
In the Expo Hall, DrupalCon Barcelona attendees learned more about the Ripple Makers membership and Drupal Certified Partners at the Drupal Association booth. Ripple Makers is the revamped Drupal Association Membership, supporting driving innovation and adoption of Drupal as a high-impact digital public good. The Drupal Certified Partner program recognizes and awards agencies that demonstrate significant innovation, philanthropic leadership, and contribution to the Drupal project. To learn more about the Drupal Certified Partner program, visit the Certified Partner Program page on Drupal.org.
Driving Drupal innovation through collaborationThe Drupal Association Board got together the weekend prior to DrupalCon for their bi-annual in-person board meeting. Topics of discussion included Drupal CMS, its roadmap, and the overall strategy and ambitions for Drupal. They held their public board meeting on Wednesday at DrupalCon to announce the newest Board Members: Alejandro Moreno, Sachiko Muto, and Stella Power. During the public meeting, CEO Tim Doyle also announced the launch of a new program, Adopt-a-Document.
On the last day of the conference, Drupal experts stayed for Contribution Day, where they worked on Drupal’s next exciting innovations. Drupal’s community has over 100,000 passionately committed users driving innovation of one of the world’s largest open source projects. Many social events occur in the evenings at DrupalCon, including Drupal’s pub quiz called Trivia Night on Thursday.
At the closing ceremony, the city for DrupalCon Europe 2025 was announced: Vienna.
About the Drupal AssociationThe Drupal Association is a nonprofit organization focused on accelerating Drupal, fostering the growth of the Drupal community, and supporting the project’s vision to create a safe, secure, and open web for everyone. The Drupal Association also administers Drupal.org on behalf of the Drupal community. You can also support the continued success of the Drupal project by getting involved. Learn more.
PreviousNext: Next level theming with Pinto
Discover how to take your Drupal theming to the next level with the brand-new Pinto module.
by adam.bramley / 27 September 2024Pinto is a new module written by Daniel Phin (dpi). At PreviousNext, all new projects use it to dramatically improve the developer experience and velocity for theming Drupal sites.
Pinto allows all theming logic to be encapsulated in reusable Theme Objects, removing the need for many traditional Drupal theming architectures such as theme hooks, preprocessing, field formatters, and even Display settings. It can easily integrate with tools such as Storybook to reuse templates—eliminating duplicated markup and making your frontenders happy!
This blog post assumes the use of a design system tool such as Storybook, which will help us paint a picture of how Pinto can level up your Drupal theming.
Issues with traditional Drupal themingTraditional Drupal theming might look something like this:
- A Frontender builds components in Storybook, containing CSS, JS, and twig templates
- A Backender implements one or more of the following:
- Theme hooks in a module, profile, or theme
- Templates are copied from Storybook and massaged into Drupal’s theme system
- A field formatter injects field data into the theme function
- Preprocess hooks massage or stitch together other data
- Twig templates contain logic to further massage variables for output in twig
- Entity view displays wire up the formatter
This approach can lead to several maintenance issues in the long term.
Have you ever looked at a page and wondered how something got there? Twig debugging can help track down specific templates, but it can be difficult to pinpoint exactly how a particular piece of data made its way onto the page.
Pinto eliminates all of this guesswork by allowing you to break components down into theme objects (i.e. PHP classes) and encapsulate all logic in a single place to be reused anywhere that component is needed.
Pinto basicsIn this section, we’ll discuss how to set Pinto up and explain some of the architecture of how the module works.
First off, we need to install Pinto:
composer require drupal/pintoPinto automatically discovers new theme objects inside designated namespaces. You can define any number of these, but we’ll use a single namespace called "MyProjectDs" (Ds stands for Design system) for our purposes. For Pinto to discover the objects in this namespace, you need to define the pinto.namespaces parameter in your services.yml file:
parameters: pinto.namespaces: - 'MyProjectDs'Pinto Theme objects are registered via a PHP Enum inside the namespace defined above. This Enum must implement \Pinto\List\ObjectListInterface. The Enum should also use the Pinto library’s ObjectListTrait which contains logic for asset discovery and automatically registering theme definitions from your Theme objects.
The enum will look something like this. We recommend placing it inside a module dedicated to your design system. For this demo, we’ll call it my_project_ds.
namespace Drupal\my_project_ds\MyProjectDs; use Pinto\List\ObjectListInterface; use Pinto\List\ObjectListTrait; enum MyProjectList: string implements ObjectListInterface { use ObjectListTrait; }You’ll need to implement at least three methods on this Enum:
- templateDirectory - This tells Pinto where to look for the twig template for the specific theme object it is registering.
- cssDirectory - This tells Pinto where to find css files. For this demonstration, we’ll assume your design system outputs all CSS and JS files into a libraries directory inside the project's web root.
- jsDirectory - As above, but for JS files!
For the latter two methods, it’s as simple as defining the root directory where your compiled CSS and JS files are output. We usually just define these as:
/** * {@inheritdoc} */ public function cssDirectory(): string { return '/libraries/my_project'; } /** * {@inheritdoc} */ public function jsDirectory(): string { return '/libraries/my_project'; }The templateDirectory method must return the directory in which a specific component’s template lives, which can be complicated since, in a Storybook setup, each component will most likely live in its own directory. For this logic, we can use Attributes on the Enum case to tell Pinto where the template is for each Theme object.
Before we talk about Attributes, let’s look at an example of how to define a Theme Object in the Enum itself.
Each Theme object must be defined in the Enum as a case.
NOTE: Clear the cache after adding new cases for Pinto to discover newly added theme objects.
In this example, we will define a Card Theme object:
namespace Drupal\my_project_ds\MyProjectDs; use Drupal\my_project_ds\ThemeObject\Card; use Pinto\List\ObjectListTrait; enum MyProjectList: string implements ObjectListInterface { use ObjectListTrait; #[Definition(Card::class)] case Card = 'card';This definition uses the Pinto library’s Definition attribute to tie the "Card" case to our Card theme object. By default (via the ObjectListTrait), the backed-value of the case (i.e. the string value assigned to it) is used as the template name, library name, and theme definition name.
Now, back to our templateDirectory. We can define our own Attributes to make it easy to specify which directory a component’s template is in.
In a perfect world, we want to reuse our frontender’s twig templates in the component’s Storybook directory. Storybook generally lives outside of your web root, but with a few helpers we can easily target these templates. Let’s assume your Storybook directory structure looks something like this, where components is at the root of your repository:
We can utilise the Components module to give us a handy twig namespace to use. Let’s define this in our my_project_ds.info.yml file:
name: My Project Design System description: 'Provides Pinto theme objects for my project.' core_version_requirement: '>=10' type: module components: namespaces: my_project_components: - ../../../../components/srcThis means Drupal’s theme engine will be able to use @my_project_components to target the root of your Storybook components directory.
NOTE: This assumes the module is inside app/modules/custom/my_project_ds, hence the four parent directories.
Next, we need to define an attribute that takes a component path and will be used to wire up our Theme object with the directory it lives in.
The Attribute class:
namespace Drupal\my_project_ds\MyProjectDs; #[\Attribute(flags: \Attribute::TARGET_CLASS_CONSTANT)] class MyProjectComponent { public function __construct( public readonly string $componentPath, ) { } }Now on the Enum case, we can add the attribute:
#[Definition(Card::class)] #[MyProjectComponent('Components/Card')] case Card = 'card';Then we just need a sprinkle of reflection in the templateDirectory function, and we’re done!
/** * {@inheritdoc} */ public function templateDirectory(): string { $reflection = new \ReflectionEnumUnitCase($this::class, $this->name); $definition = ($reflection->getAttributes(MyProjectComponent::class)[0] ?? NULL)?->newInstance() ?? throw new \LogicException('All component cases must have a `' . MyProjectComponent::class . '.'); return \sprintf('@my_project_components/%s', $definition->componentPath); }While this may look scary, it’s not! We’re simply getting the MyProjectComponent attribute from the case and appending the componentPath (i.e. the string "Components/Card" parameter above) to our custom twig namespace.
After this setup, it becomes extremely easy to add new theme objects, vastly speeding up your theming development. Let’s look at how to do that now!
An example Pinto componentFollowing on from the previous example, let’s set up a Card component. A Card is a fairly universal component in almost every project. In this example, a Card has:
- A title
- An image
- An optional description
The twig template in Storybook might look something like this (reminder that this would be inside the components/src/Components/Card/card.html.twig file):
<div class="card"> <div class="image"> {{ image }} </div> <div class="card__content"> <div class="card__title">{{ title }}</div> {% if description %} <div class="card__description"> {{ description }} </div> {% endif %} </div> </div>At its most basic level (don’t worry, we’ll get more advanced in the next blog post!), all we need for this theme object is something like this:
namespace Drupal\my_project_ds\ThemeObject; use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\my_project_ds\MyProjectDs\MyProjectObjectTrait; use Pinto\Attribute\ThemeDefinition; #[ThemeDefinition([ 'variables' => [ 'title' => '', 'description' => '', 'image' => '', ], ])] final class Card implements CacheableDependencyInterface { use MyProjectObjectTrait; private function __construct( private readonly string $title, private readonly array $image, private readonly ?string $description, ) {} protected function build(mixed $build): mixed { return $build + [ '#title' => $this->title, '#description' => $this->description, '#image' => $this->image, ]; } }Let’s go through each part to understand what’s going on.
ThemeDefinition attribute
This is essentially Pinto's version of hook_theme. It takes whatever is inside the theme definition and uses it when registering the Theme object as a Drupal theme hook. In this example, we simply define the variables we need to pass through. Pinto will even throw exceptions when any of these variables are missing from the resulting render array!
MyProjectObjectTrait
This is another helper trait that will be used across all components. It provides convenience functions and wraps other traits to make adding new Theme objects a breeze.
namespace Drupal\my_project_ds\MyProjectDs; use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Cache\RefinableCacheableDependencyTrait; use Drupal\pinto\Object\DrupalObjectTrait; trait MyProjectObjectTrait { use DrupalObjectTrait; use RefinableCacheableDependencyTrait; public function __invoke(): mixed { return $this->pintoBuild(function (mixed $build): mixed { if (!$this instanceof CacheableDependencyInterface) { throw new \LogicException(static::class . ' must implement CacheableDependencyInterface'); } (new CacheableMetadata())->addCacheableDependency($this)->applyTo($build); return $this->build($build); }); } abstract protected function build(mixed $build): mixed; }First up is the __invoke function. This is a PHP magic method that is becoming more and more common in PHP codebases (including Drupal!). It’s run when calling an object as a function. The pattern lets us “invoke” Pinto Theme objects like this (note the brackets at the end):
$card = new Card('Title', 'Description', $image); $build = $card();We then have an abstract build function, which you can see in the Card example above. We simply take the properties passed to the card and output them into the theme variables.
Our object trait also extends Pinto traits and core's trait for adding cacheable metadata to a Theme object—more on this in the next blog post!
Constructor
The constructor on the Theme object lets us define strictly typed properties for what we need for each theme variable. Generally speaking, we use a mix of constructors and factory methods to create Theme objects and then invoke them, as we’ve seen above.
What about CSS and JS?
Pinto has built-in attributes that automatically include CSS and JS when a theme object is invoked. This is done by adding the attributes to the Theme object class.
use Pinto\Attribute\Asset\Css; #[Css('card.css', preprocess: TRUE)] final class Card implements CacheableDependencyInterface {Based on the cssDirectory method on our MyProjectListenum, this will include the /libraries/my_project/card.css file automatically whenever a Card Theme object is rendered. There is also an equivalent Js Attribute.
How to render Pinto Theme objectsIt may seem like a lot of set up upfront, but hopefully, you can see that once the initial enums and traits are established, it’s very easy to add new Theme objects.
Now that we’re all set up, we can invoke this Theme object anywhere on our Drupal site! We can even chain/nest these Theme Objects together!
You may have noticed that the $image parameter for the Card was an array. Invoked Theme objects output a render array, so we could do something like this:
$image = new Image('path/to/image.png'); $card = new Card('My Card Title', 'My Description', $image()); return $card();Where Image is another Theme object. Or, you can simply use Drupal’s existing render arrays to theme an image!
Next upIn this blog post, we focused on the basic principles of Pinto. We also looked at how to hit the ground running with some nice helper functions and integration with a design system such as Storybook.
In the next blog, we’ll look at integrating this with bundle classes to streamline theming and really make Pinto sing! Following that, we'll also be doing a comparison with Single Directory Components (SDC) and discuss why we prefer to use Pinto instead.
Drupalize.Me: Drupal AI: How to Set It Up and Try It Out
After watching the Driesnote earlier this week, I wanted to try and play around with the AI tools that were demonstrated. Here's my notes so far. And instructions on how you can set it all up to experiment with the new AI tools in Drupal.
joe Thu, 09/26/2024 - 14:47Horizontal Digital Blog: Add an icon for your custom field type in Drupal's Field UI
Acquia Developer Portal Blog: Project View: Realizing the Power of Drupal's Constraint Validation
I have been working with Drupal for quite some time (from the days of Drupal-7 🙂). I was always up to-date with the new changes in Drupal APIs (at least in theory 😅 ). Whenever a new change was introduced in APIs, I checked them and tried them out.
I did the same thing for the new ValidationAPI. I checked the documentation, tried one basic Hello World type of scenario with some example code and thought it was all good.
Drupalize.Me: Adopt a Document: Sponsor Drupal CMS Documentation
The Drop Times: 1xINTERNET Shares Community-Driven Updates for Drupal CMS Search Functionality
drunomics: Lupus Decoupled Drupal: Bridging Drupal’s Backend Strength with Frontend Freedom
Metadrop: Optimizing Drupal Performance - Internal Page Cache
The Internal Page Cache is a core module in Drupal responsible for caching pages requested by anonymous users.
When a page is cached and an anonymous user makes a new request, Drupal does not need to perform any rendering or page-building processes. It simply retrieves the rendered page from the cache and sends it to the client.
The reason it only applies to anonymous users and not authenticated users is that the page returned to the client must have exactly the same content for all users.
In the case of authenticated users, although part of the content may be the same for everyone, there are always elements that can vary, such as the user block displaying the user's name or other user-specific information.
For these cases, there is the Dynamic Page Cache module, which handles caching for both anonymous and authenticated users.
Functionality Cache BinFor storing and managing cached pages, the Internal Page Cache defines its own cache bin called “Page,” meaning that cached objects are stored independently of other existing cache systems in Drupal.
…Drupal Association blog: The Drupal Association Announces 2024 Board Election Winner and 2 Additional New Board Members
The Drupal Association is saying goodbye to two board members and welcoming three new board members who will join the Drupal Association Board.
The Drupal Association extends a sincere thank you to Nikki Flores and Nick Veenhof for their service and dedication, not only to Drupal, but to the Drupal community. Thank you for everything you both have done while on the Drupal Association Board! Your time spent on the board made such a difference to the future of the Drupal project, and we thank you all for participating with grace, thoughtfulness, and insightful contributions.
The Drupal Association would now like to congratulate our newest board members, officially announced during the recent public board meeting at DrupalCon Barcelona:
An additional congratulations to Alejandro Moreno for winning the community-elected seat during our 2024 At-Large Board Election! We cannot wait to see what amazing things Alejandro will accomplish while on the Drupal Association Board. We invite you to get to know Alejandro and learn more about his background.
I'm deeply honored to have been elected to the Drupal Association Board. Thank you to everyone for your trust and support—I look forward to serving our incredible community! - Alejandro Moreno
We extend our gratitude to all the candidates who participated in the 2024 election. On behalf of all the staff and board of the Drupal Association, a heartfelt Drupal Thanks to all of you who stood for the election this year. It truly is a big commitment to contribution, the Drupal Association, and the community, and we are so grateful for all of your voices. Thank you for your willingness to serve, and we hope you’ll consider participating again in 2025!
Detailed Voting ResultsThere were 7 candidates in this year’s At-Large board member election.
707 voters cast their ballots out of a pool of 2741 eligible voters.
Under Approval Voting, each voter can give a vote to one or more candidates. The final total of votes was as follows.
Candidate
Votes
Albert Hughes 197 Will Huggins 154 Alejandro Moreno 282 Janna Malikova 234 Kevin Quillen 189 Matthew Saunders 188 Dominique De Cooman 208