March 29, 2020
Delivering Headless Commerce

Delivering Headless Commerce


– [Matt] So, welcome to
Delivering Headless Commerce. My name is Matt Glaman. I am a product lead at Commerce Guys. I am a Drupal Commerce co-maintainer. You might have read my book, the Drupal 8 Development Cookbook, and you may have used ContribKanban, which is a little tool
I wrote a few years ago to help with sprints,
a few things like that. So, let’s first dive into
what is headless commerce. So, if you were in
Ryan’s session yesterday we kind of touched on this a bit. The big thing is that it
separates the customer experience layer from the business
application, right. So, you’re separating concerns, which, by separating those concerns, you can support any number
of different front ends from a single back end. Think, if you shop Amazon, right, they have their mobile app, they have the web app, they have Alexa, they have Dash Buttons. And you can evolve and scale
parts of your e-commerce system without implicating the,
impacting the others. Take anything, like you want to, if you decouple something,
you can work on it without ruining something over here. So, let’s look at some quick examples of what is a headless
commerce implementation. So, the basic one is, everybody here has heard of React JS, correct? Whether you like it or not,
’cause I know there’s a few, and there’s the framework wars. Here’s an example of
using a React front end that interfaces with a
Drupal Commerce to back end. So, right here, this
widget is actually powered by GraphQL, and the product page is a mix of GraphQL and JSON API. All these are Drupal modules
I will touch on in a moment. We have our cart API. And so, you add it to the cart. So, this is all JavaScript that’s talking to a back end of Drupal, which means that it could be replaced with
a, if you write in Flutter, which is a mobile app SDK,
or Vue or React or Angular, or et cetera, et cetera, et cetera, and here we’ve replicated
the Add to Cart form. And I think maybe this
one was a smidge too long. But it’s to show that
it’s a fully actionable e-commerce website that’s headless. So, as we go through it even has the cart. So, here it applies a promotion, which this is now on Drupal forms. There’s no any of that
layer back in there. So, it applied the coupon, and one thing that we will be working on (mumbles) is what it’s like to do a
decoupled checkout, right? ‘Cause you want that fancy accordion. You want it to be slick and fast. A lot of that (mumbles),
to be slick and fast, it has to be in the
front end on JavaScript. Or, you could do a checkout offsite, where it brings you to
that singular back end and you actually, this is Drupal. This is regular Drupal Commerce, regular Drupal checkout, but
your different front ends could go to that one checkout form, and have all the data here. But, okay, JavaScript is cool, but let’s take it, maybe as to show this in all craziness, who here
has used Panopoly on Drupal 7? All right, who’s used panels in Drupal 7? Cool, this (mumbles), oh,
more people were late to this. So, here’s an example of
where we built C tools content types, which
are your panel’s panes, and it renders in data. So, there’s an Add to Cart form. As it change the Add to Cart
it changed the SKU value. So, now it’s blue large, Add to Cart. This is all just jQuery, by the way. So, this kind of shows the robustness of this idea of multiple front ends for your singular back end, and I’m back at that singular checkout. Same back end, same checkout form, but I could have different entry points and different ways to shop. So, there’s a few examples
of what we mean by, what I mean by headless commerce. It’s this idea of
interacting with your one e-commerce site in multiple ways. There’s some challenges to this, and that’s gonna be the whole part of delivering headless commerce. When it comes to
e-commerce data in general, this isn’t just Drupal
Commerce, it’s structured. It has a very structured
data model, you know? You have pricing, you have
products, the variations. Are they blue green? Are they small, medium, large? You have the multiple
relationships and layers. You have an order that belongs to a store that has order items that
say what you purchased. There’s this relationship model that needs to be presented in an API, and the difficult part there
is how do you differentiate what’s part of the presentation layer and what’s just actually part of the data? And that’s the whole concept of decoupling is insuring your data
layer can be represented in normal Drupal, like we do right now, or also in this API context. We have some available options
for implementing these. I’ve named a few of them off. There’s GraphQL. Raise hands, how many
people know what GraphQL, have heard of GraphQL? We’ll just do it with GraphQL. Most people have. If you haven’t, GraphQL
was invented by Facebook. That turned into an open standard, and has kind of flourished, ’cause, we’ll see why in a minute. JSON API, there has been
a lot of buzz about it. It landed in core for 8.7, which is exciting for
people that really want to use the module and also for those that want to integrate with it. JSON API is a spec. It just says here’s a REST
API that should follow this implementation standard, and we’re gonna show
why that’s a good thing in the next few slides. Core has always shipped with the, shipped with the RESTful
Web Services module, which doesn’t provide any, that’s the little asterisk at the bottom. You can enable it, but it
provides you no real help. It gives you a way to do an API interface, but you do have to
write the code yourself. There is also the JSON RPC module, which I don’t cover in here,
but it is one of those things that I wanted to at least mention it. It creates a remote procedure
call API input using JSON, but kept that out of the
scope of this session. So, first let’s talk about catalogs. Let’s take standard Drupal, right? You have Views and Search API, and then you render your products, and you get this nice, pretty page, right? You get the image, you get the price that’s properly formatted,
you get the title. You can click on it, go to the product. You have faceted searching in the left. If you’re not familiar with facets, Search API, let’s see,
so Search API and facets, that creates an abstraction
layer for talking to a search index like
Solr or ElasticSearch, which lets you not have to query
your database for catalogs, and then the facets just
provide a really awesome way to navigate that catalog. And this is kind of what
you get out of the box. Views, this is is powered by Views. So, Views does a Search API query, gets you your data. Those index documents are
enriched with entity data from Drupal, and you get
this nice fancy catalog. So, that’s one of the areas of concerns. You know, where you have
a highly customized query for products, ’cause that’s
how people are shopping. They want to drill down. You want it to be able to filter what people are looking for. As I said, they’re
powered by search indexes, because you could use your database, but then your database is
doing these epic queries, and yes, SQL, like MySQL, Postgres, all those are good for epic queries, but this is the job for a search index. Search API does provide a good stop gap of a database version, so
that you could use that. Faceted searches, like I
said, for fine tuned browsing, so that way you can say I
have this list of products, and the (mumbles), I know my
catalog has red, green, blue, but only the available
products are in red. So, I don’t want to show blue or green, because those aren’t valid options. Then you have to worry
about variable pricing on the catalog context. Do they have a coupon available? Is it Black Friday, and
everything is 20% off? Do we need to show that in the pricing? ‘Cause the default price is 9.99, but for Black Friday it’s 7.99, and you need to be able
to show that on the site. And then product availability, because you don’t want
to sell 10 extra items than you have and make eight phone calls saying sorry, we don’t have the product, and then you get cranky customers, and that’s just a big old mess. So, first I want to talk about JSON API, when it comes to catalogs. First, I really do love the JSON API spec, ’cause it makes querying in
a RESTful context amazing. It’s just very verbose, and it allows you to include reference to entities. You just do this little include parameter, which I’ll show later on,
say I want this field, and it brings in that entity for you, so you don’t need to do multiple requests. In a typical RESTful
interface you would have to do a request for each resource, which, let’s say you have a product, and you have a very, you
might do six requests just to get all the product data, and that’s kind of a pain. The JSON API, the spec
helps (mumbles) that. It’s declarative and discoverable. When I say that, it’s like hypermedia. It provides a lot of links. So, you reference this item. I will give you the link to go fetch it, so you don’t have to hard
code it into your client. There are some caveats. It doesn’t support a
mixed bundle collection. So, all the routes it
generates are /node /article. You can’t go to /node and
get your articles and pages. You would go /node /article /node /pages, and that’s kind of represented in here in my little, you know,
green product/simple, product/clothing red, hmm, /product. You can’t just get a collection
of all your products. But you can, with a little bit of legwork. It could integrate with Search API. Right now it does not. There is no way to use JSON API to talk to Search API to get your
search index documents. What I’ve been told by a
few people is just query your Solr index directly. It’s like, but (mumbles), I don’t want to. I want to just have my middleware. Where I found that just this
knocked it out of the ballpark, and that’s why the demo
uses GraphQL, is GraphQL. So, it’s a query-like language API. As you can see up here, so it’s, you say commerceProductQuery. I gave it some filters,
like I wanted it to be from the audio and film category, and then I just kind of
fleshed out my query a bit. So, this is all of the entities. And where it says …on
thing, like CommerceProduct, or CommerceProductClothing,
that’s called a fragment, and that’s just a piece of
GraphQL terminology that says when you hit this schema
type, do these things, and it knows that these
are the available fields, which allows us to do
cross bundle querying. So, that limitation that
we hit in the JSON API was blown away in GraphQL
when building a catalog. Because I say all right,
on CommerceProduct, well then, we have simple products which don’t have attributes,
there’s no color or size, but we have clothing too that we sell, and these allow you to buy,
purchase by color and size. So, right here, it’s almost
like an if else statement, like a bunch of if statements in my query. If you run into this document,
pull in these fields for me. Now, we’ve all built Drupal
sites and we know that we might have three content types and they share the same
four fields, all right, and it’s like, okay, well, you need to pull those into context. So, that’s honestly what a lot
of the boilerplate here is, and that’s why I collapsed some of them. Luckily, with GraphQL you
can do reusable queries. I haven’t dived into it quite yet, but there’s this concept of a query map. You have to download this other tool, point it at your server,
point it to a saved query. It generates this JSON that you can import to your Drupal site and say I want my catalog query, and done. That can be useful once you
have everything set in stone, but if you’re developing you can use this, and I did skip over the GraphQL IDE, which I, it’s GraphiQL, and
I think it’s like graph equal is how they want you to pronounce it. I have no idea. If somebody does know, after
the session please correct me. – [Woman] Graphical. – [Matt] It’s graphical! That makes way more sense! (man chattering)
(people laughing) So, with JSON API being declarative, you can just get a response
and know how to work with it. With GraphQL you need to go into an IDE. Now, it’s just a little
JavaScript text editor that’s really smart, and build your query. So, that is one of the
caveats that you need to know how to query it, and you need
to pre-build your queries, not inside of your main client. GraphQL does have a
Search API Contrib module, (Matt applauding)
which made me excited. (Matt mumbles) There’s caveats there. So, it allows you to do
full text query support. So, when you say full text,
that’s where you can do text matching, and you can get all kinds of craziness into it. You can have multiple conditions, and you can support facets. So, I’m gonna bring it back
to the facets site, statement when I said you have
five products available. None of them come in red or blue. Why show those values to filter on? When you include those facet operations it returns those values, but there’s also a little asterisk there. It’s not like working
with the facets module, or Search API in Drupal. It returns all the different index values. So, how I had that product category that said audio ampersand film, the facet showed audio
and film as two options, ’cause I had them as full text, instead of I want audio
and film to be an option. So, you have to rethink how
you set up your Search API for just Drupal to now where
you have this instance, and I’m hoping to help
find ways that we can build a Commerce GraphQL module to help kind of streamline some of these. So, at X, the other part I
said before is Search API, when it gets that query it actually loads your Drupal entities and the Search API is basically just providing
(mumbles) what are the valid IDs to show? Cool, go back into Drupal and
do the normal entity loading. With the graph QL Search API module, it only returns data from the
index which is performant. We have done this on
Drupal commerce sites, where for performance we
de-normalize the entity, so instead of having to
say load all the fields, we’d say here’s the
structure of the product, put it into the Search API
index, and then render that. So there is actually a
performance boost in there, but again, you have to rethink
how you index your data, which means that you can’t
do dynamic pricing as well, because that pricing is
set to what was indexed, and then you just, you
know, it’s not an impossible challenge, but you need to say, all right, after I get my products how do I resolve the proper pricing to display? Little tricks that you
have to come up with. So, it’s more like a
middleware, or a proxy I think is a better word to
say, to your Solr index, instead of directly querying it. So, next we’ll go to
product display pages, or PDPs as I’ve come to
hear them called before. I just said product pages. So, let’s go to some
area of concerns there. This is always the hardest part is it’s marketing content, but also has this purchasing and product data, right? It’s like, I want to be
able to do, put stuff here. It should be a marketing landing page, but I want people to buy from it. Well, if you make it a marketing page, people don’t know how to buy from it, but that’s the constant struggle, and that’s also what just
makes them, you know, a little bit more of a challenge, no matter how you’re implementing them. You have to represent
the available variations and their field values, right? If it’s a green product you
want to show the green images. If it’s red, show the red images. If it has different pricing,
be it based on size, let’s say there’s a up
charge for the bigger box, you need the price to
update and show that. How do you want the product
attributes to display, right? You don’t want it to just
be a bunch of drop downs. You might want little color swatches. Maybe you want it to be images. And then there’s the whole
area of Add to Cart forms, which I have as the next section. So, we’re gonna swing back to JSON API and kind of reiterate a few things. It’s all about linking resources. So, in that blob you’ll see it says links, self, and then (mumbles). Now, throughout it will have
those kind of documents, and that says this is how
you fetch that resource, and it always includes them
because even if you call this product directly,
what if you called it from another call and you
need to know that URL? So, you can build like a front end client that can just go crawl it out. That’s why it’s called hypermedia, like the web is a bunch of links, which I do recommend. I’ll add it to the slides after. I meant to link it into here, but about hypermedia, so you can learn more about all of that. So, and it supports includes with, I thought I got
it into this snippet, but the relationships,
anything in the relationships, it could be like question mark includes. So, you could get the field brand, the categories, the
stores, the variations, and you can limit what
fields are returned. So when you do this, I did an experiment, and the next slide shows the return without the page load size. If you take I want this
product and include the brand, the categories, the
variations, the stores, it was like 50 kilobytes of JSON data, but once you do a proper
filter, it was down to six. That’s because it has
all the metadata in it, and it included the created
and changed timestamp, the default line code. There’s things that need to be, that, you know, it would be nice if they were automatically excluded, which will be coming down the pipeline. But once you tell it to exclude those, because they’re not necessary, it’s a much more reasonable
payload to work with. But again, the routes require
the bundle to be present. I have it in a little
footnote there if you have a product ID you need to
know if it’s a simple product or a clothing product or a
e-book or a physical book. We’re working actively
with the JSON API team to resolve this. William (mumbles) and
Gabe are very engaged with myself to help find out
how we can work around these, and it’s not just us either. The Admin UI initiative is using JSON API, but they’re having this
issue on like the admin, the content admin page, right, /nodes. They had the same difficulties, and we are working to solve this. Now that it’s in core, it’s
much easier to work with, because they’re not trying
to get JSON API into core. So, here’s an example. The color scheme for
insomnia is a bad one, and I realize on the, up
here I couldn’t get it to not be yellow, but on
the left I hope you can see all of the query parameters that help bring things down, and
then load the included. Is that impossible to read? Yep? – Yeah.
– Yeah. – [Matt] Sorry. – [Man] I can read the
caption at the bottom. – [Matt] I will be uploading the slides, and I’ll try to share it
out to show the example. Apologies. And then we’ll go to GraphQL
for the product pages. Again, just like catalogs, it’s pretty much the same thing. You know, it allows supporting
the included entities. You can, again, specify
the required fields. So in here, I have it
pulling in the price, and then on the product
variation of clothing including the images,
the specific derivative. So, it’s actually given me my image style with the URL, the width, and the height, so I can pass it into my
little React component, and render the image appropriately. You could take one of
those fancy placeholders that does the gloss over,
’cause you know the width and the height, and then
once the URL is fetched, load it in. Add to Cart forms, you know,
they’re kind of critical, ’cause you want people
to buy your products, and be able to take their money. So, you need an Add to
Cart form for that to work. Again, these are probably one the, some of the hardest
parts, besides checkout. Not hardest, but just the most
critical parts to get right. I brought up before attribute select lists versus fancy formatting. Do you want that select list? Do you want select list, radio buttons, little rendered swatches? Who knows, I did a site
where they sold car seats, customizable car seats, and
they wanted the color options to be actual images of the
versions of the car seats, not just the color swatch, but
an image they could upload. You also have to identify the
currently selected variation. Okay, you’ve picked blue,
green, and then let’s say there’s this third optional attribute, and you have to figure out how to resolve what they are purchasing based
off what they’ve given you, and then custom order item fields, this is one of the most robust
things about Drupal Commerce is you could say, all right,
you’re gonna buy a T-shirt, well, you can custom engrave it. So, I’m gonna give you a text
box to type in 30 characters, so we can print it on your shirt. Or, I worked on a project
where they had trophies, and you could upload an image
that they would etch onto it. That’s one of the huge
benefits of Drupal Commerce, and also something that
needs to be displayed on the Add to Cart form. As I’ve said, it’s an,
it’s a critical part, which also makes it challenging
to provide abstractions and reusable solutions. And I’m gonna go through a
few examples of different Add to Cart forms that I’ve built, and not once have I been
able to reuse a single thing, except for this like functional array map, array reduce, array filter, crazy snippet. So, let’s go into our current demo. This is Drupal Commerce with
our cart fly out module, and the cart fly out module
progressively decouples Drupal. It just says, you know, instead of Drupal still
serving the formatting, but then JavaScript pops in, and is like, I’ll do the Add to Cart
form and your cart block. So, this is using jQuery,
Backbone, and Underscore to kind of render things out, and for the most part
it’s near feature parity to our pure PHP normal static version, except for order item fields, because how do you know that needs to be a text field or a select list? There’s all these questions. We’ll go to the React demo. This is what we just saw. I didn’t get a chance to
build in the image variation swapping, because you need to
know all the available fields. Go into there, swap it out, but it does at least know
if I pick green then small, or large, okay, I didn’t hit Add to Cart. But it knows what I properly added, and then last year I built a GatsbyJS site that reads the Drupal Commerce back end and builds an Add to Cart form, and I was able to get the
image variations to work and change out as they go, and a lot of that’s because Gatsby reads from a remote source and creates
their own consolidated data format that you read via GraphQL. So, it was, it made it easier
to know certain things. Then to the good old Drupal 7 version, this is pure jQuery,
(laughing) which was fun managing the state, but I was able to fetch
the product via JSON API, and I was able to know all
the different variations, and I could, my select list said this is the active view you ID. Tell me the product fields
that I know are available. So, the challenging part as
a framework maintainer is to divide the abstract
and reusable components for purpose built front
ends, and JavaScript, like Drupal, you can
customize a Twig template, you can do a pre-process,
do all these things to alter how a page looks. It’s not that way in JavaScript. You build on, what does React call it, high order (mumbles) components, sorry, where you just kind
of stack and build things up. You can’t just swap
something out in the middle and say (mumbles) use this component. There’s tricks to do that. So, the idea is how can we
help people build these? We’re gonna figure that out, and hopefully I’ll come across some really super front
end people that say you can’t just build good examples or we find ways to just
help make this easier for everyone to deliver
these kind of projects, without having to go through
this kind of code salad. (man laughing) You know, it’s not going to be readable, but on the left is the back door backbone, and underscore, the middle is the, the just pure jQuery and
on the right is GatsbyJS. The only thing that’s reusable is knowing what is taking (mumbles), the attributes, and trying to figure out
the proper variation. So, the next step is carts. This is if you caught my
Road to Headless Commerce last year, this was our big focus. All right, how can we
at least manage carts in a decoupled fashion? Because, why are carts hard? They need to belong to anonymous
and authenticated users. Users can modify their carts, but they shouldn’t be
able to just delete them, or edit things that
aren’t the order items. You need to have constraints
on product availability. Should they be able to
add something to the cart that’s not available,
(mumbles) and checkout, and people shouldn’t be able to edit other people’s carts, right? I shouldn’t be able to
figure out your order ID and be able to start
adding things to your cart mid-checkout, and then
you pay all this money. So, with the Cart API
module, it’s a bespoke module that we built that uses
core’s REST, REST module, and it helps hide some of the internals for adding something to the cart. It provides a Get collection to give you all the active carts for a current user, and we say that because you
could have multiple carts. Our system does allow that. A Post that lets you say I have this person’s (mumbles) entity. I have this quantity. Add them to the carts for me, and Patch to update those quantities, and that’s the little
sample of fake requests. But what I came up with
over the past six months is this idea of a cart token. As soon as you’re in React and
you’re doing CORS requests, cross origin requests,
cookies are ignored, which means your PHP session is ignored, which means that you’re always creating new carts every time you add an order. So, the workaround is you
basically pass this custom token as either a header or query parameter. We couldn’t use the authorization, because what if you are using O auth or other authorization headers? And this basically allows
it to work in a cross domain and cross origin interface. So, even if your React
app is on the same domain, it’s still cross origin,
’cause it might have front end dot and API
dot are cross origin, or you could have myawesomebrand.com with myawesomebackend.com, and they won’t even communicate cookies at all. I found out Firefox and Chrome will, but Brave and Safari will not. So, luckily I caught that
before doing the demos, and everything just broke. JSON API, this has been the
point of communication the most, is (mumbles) really, they
really want the cart API module to just use JSON API. I do too, because it provides all those fancy includes and fields. You can really customize
your cart fly out. Again, there’s no cross bundles, so you can’t just say /cart. We’re working on it. I have it working. It’s broken for JSON API in core, but it worked for JSON API 2.3. So, we’re getting close. Now that it’s in core, I
can just make things work. So, it provides that
custom cart collection. Right now, adding a product to the cart requires knowing I need
to create an order item, then attach that order item to the cart. It’s very internal. You need to know the internals. We want to abstract that out into a more, and still keep it a RESTful interface. GraphQL, you can get the carts, but you can’t add anything, ’cause in GraphQL
they’re called mutations. I didn’t get a chance
to experiment with this, because they actually ripped mutations out of the core module
to put it as a contrib, because there’s just hard
things about updating content. And I totally meant to put, I mean, that’s a query for getting a cart. I wanted to put a fake
mutations query in there, but I had this demo-able and working. Getting close on the time, so let’s go to the Checkout and More. This is the thing that made me happy, applying coupons headlessly, because coupon validation is hard, and I could not figure it out last year, and it was like my number
one we need to figure out how to make this possible
outside of the checkout form, and more reusable. So, we actually use
Drupal’s validation system. It implements Symphony’s. We added a constraint to the field. So, when you go to
validate an order it says okay coupons, do you apply to this order? Are you still available? If it’s not, throw an error. If it is, get the promotion. Is the promotion still valid? If not, throw an error,
and all of the API modules validate entities before saving. So, if you apply an invalid
coupon code it throws an error and it just quote unquote works. So, this got me really excited. One thing that’s not shown
in here that I’m working to get pushed into our code
base is this order subtotal. Right now, orders only
have their total price. The subtotal and all the adjustments are calculated in a field formatter. Can (mumbles) those
formatters over an API. So, it uses a computed base field and this just got really technical, but the point will be hopefully soon you’ll be able to
get that information from a field instead
of this extra process. And finally, checkout over the API. I went down this road last year, and just found a bunch of
our core underlying problems, the same problems that Drupal core had when the API First initiative kicked off. Taking stuff out of the
presentation layer and moving it more and more down to the bare
minimum of the data layer. It’s hard because there’s a lot
of things to put down there, but I would love it if by,
you know, DrupalCon Amsterdam we have a working prototype, some way. Don’t hold me to it, but at least somewhat of a working demo, and
really it’s just taking form validation logic and putting it into the core data level. We have all this logic,
but it’s stuck in a form, and form validate, and form submit. So, now we need to see
how we can abstract that down to a more primitive level. That, To Headless and Beyond, woo. What’s next? That top one actually got
committed by (mumbles) today, and that prevents you from needing an internationalization library. When you fetch a price
it’s gonna auto-format it into your currency. It’s gonna provide a formatted
property that formats that into your currency and locale. So, if your API is smart,
which Drupal Commerce, because of Drupal, is really
great at multi-currency and multi-language, it
will say oh, it’s in euros? Cool, you’re in the United States. Well, we’ll put the euro sign first, and then one comma zero zero. Oh, you’re not here in Europe. We’ll put one dot zero
zero comma zero zero and then the euro sign and the API will just do that for you, instead of having to
write the crazy JavaScript I had to write when first doing the demos. The next step is improving query access and entity access. We already, Entity API now
has implemented the Drupal 7 query access, which means
that we can take a lot of our logic and helpers out of forms and the field formatters and
put it into the core logic. Coupon validation constraint,
that’s what I just demoed. I think we’re gonna hopefully get that in within the next week, and
then it’s just coming up with the API endpoints. In development, this
is the technical part, the computed base fields
that just say this isn’t stored in the database,
but when you fetch me, we’re gonna run something
and give you the data. This gives you the order
total, adjustments, resolved pricing. So, that comes into the
dynamic pricing aspects, and then coupon redemption, what I showed, you would have to apply
it with that coupon ID. I want to find a way that
you can actually give that coupon code and apply it without needing a custom REST endpoint, so that way JSON API and
GraphQL could use it. And then finally, I want to make sure to give a shout out for this. Contribution Day is
tomorrow from 10:00 to 4:00. Bring a laptop, there will be coffee. The three major areas will be
the D9 Readiness initiative, (mumbles), the out of the box
demo is trying to translate their content to Spanish. So, if you are someone who
speaks Spanish or knows it, it’d be great to help get your assistance to really show this
multilingual capability that Drupal has, and also
the Admin UI initiative for the (mumbles) theme,
there, if, so if you’re a CSS wizard or a design wizard,
there’s work for you. There are things you can contribute to. With that, I don’t think I
have any time for questions. Do I have, nope, okay, done. There you go. If you want to give it a spin
go to react.demo.centarro.io. That’s the main demo there. If you want to see a
progressive decoupling demo, which is our default, it
is demo.kickstart.com, and if you are curious
for the GatsbyJS one, it’s not too flashy, it’s
gatsbyjs.demo.centarro.io, and thank you very much. (audience applauding)
Captions made possible by ClarityPartners.com
Chicago area Drupal consultants

Leave a Reply

Your email address will not be published. Required fields are marked *