Joplin as a shopping list

Thanks, it works great !

It seems to be a problem specific to iOS, on Android I don't have transparent text. Unchecked items are regular, and checked ones are bold. Exactly like it should be.

You can also use <hr/> elements to separate the categories. The following works perfectly for me (I didn't touch your CSS) :

- [ ] Shopping mode
---
- [ ] **Fruits**
- [x] Apples
- [x] Oranges
---
- [ ] **Vegetables**
- [x] Tomato
---
- [ ] **Others**
- [ ] Toilet Paper

The only (slight) inconvenient is that when there are categories with no checked items, in shopping mode there a several <hr/> displayed, one after another (one per category). But I can live with that...

Tested on Android (on desktop I still have 2.11.11, so cannot test).

Post deleted, mistake on my part. Works fine. Sorry

Instead of using <hr> you have to use a code block as seen in my examples.
At least where you don’t want it to stay.

OK, I get it. But I don't mind separators between categories, so I stick with --- elements (e.g., <hr/>).

The following is what I use now, works perfectly on Android :

// your css code

- [ ] Shopping mode
---
- [ ] **Fruits**
- [x] Apples
- [ ] Oranges
---
- [ ] **Vegetables**
- [x] Tomato
Cheaper in shop A than shop B
(only € 1.80)
![Lorem Picsum](https://picsum.photos/200)
- [ ] Onions
---
- [ ] **Others**
- [x] Toilet Paper

An ultimate improvement would be the ability to hide by default an item's comment lines (including pictures), and to only show it on some action (for example if you click on the item itself, or something like that). But I don't think that's doable in pure CSS.

OK I found a way to avoid having multiple separators in shopping mode when there are empty categories (e.g., categories with no items checked).

So, if like me you don't mind having a separator between the categories, here's a full example :

@danielfomin96 : I think using plain separators like I do makes adding new items more easy (no need to write the --------- code like separator)

@danielfomin96 : As for the CSS code, the differences with your code are :

  • I removed the code concerning your custom separator :
/*
	Hide separator element
	*/
code {
  display: none;
}
  • And I added additional hr selectors to your "Hide Headings" section :
/*
	Hide Headings
	*/
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul,
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul + hr {
  display: none;
}
	
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  ul:has(.joplin-checkbox input:checked),
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  ul:has(.joplin-checkbox input:checked) + hr {
  display: block;
}

Full CSS

<style>
/*
	Prep mode
	*/
div:has(ul:first-of-type .joplin-checkbox:first-child input:not(:checked))
  input[type="checkbox"]:checked {
  width: 17px;
  height: 17px;
}
div:has(ul:first-of-type .joplin-checkbox:first-child input:not(:checked))
  input[type="checkbox"]:checked
  + label {
  font-weight: bold;
  font-size: 1.1em;
  opacity: 1;
}

/*
	Shopping mode
	*/
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  input[type="checkbox"]:not(:checked) {
  display: none;
}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  input[type="checkbox"]:not(:checked)
  + label {
  display: none;
}
	
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  input[type="checkbox"]:checked {
  color: black !important;
  opacity: 1;
}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  input[type="checkbox"]:checked
  + label {
  font-size: 1em;
  color: black !important;
  opacity: 1;
  font-weight: bold;
}

/*
	List Headings
	*/
ul:not(:first-of-type) li:first-of-type input + label {
  font-weight: bold;
  font-size: 1.4em;
  opacity: 1;
  display: inline !important;
  pointer-events: none;
}
ul:not(:first-of-type) li:first-of-type input {
  display: none;
}

/*
	Hide Headings
	*/
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul,
	div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul + hr {
  display: none;
}
	
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  ul:has(.joplin-checkbox input:checked),
	div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  ul:has(.joplin-checkbox input:checked) + hr {
  display: block;
}

</style>

- [x] Shopping mode
---
- [ ] **Fruits**
- [x] Apples
- [ ] Oranges
---
- [ ] **Vegetables**
- [ ] Tomato
Cheaper in shop A than shop B
(only € 1.80)
![Lorem Picsum](upload://ydNdjgBgkxXiwOp5ZPCN0De7izB.jpeg)
- [ ] Onions
---
- [ ] **Others**
- [x] Toilet Paper

Minimized CSS :

<style>

div:has(ul:first-of-type .joplin-checkbox:first-child input:not(:checked)) input[type="checkbox"]:checked {width: 17px; height: 17px;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:not(:checked)) input[type="checkbox"]:checked + label {font-weight: bold;font-size: 1.1em;opacity:1;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) input[type="checkbox"]:not(:checked) {display: none;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) input[type="checkbox"]:not(:checked) + label {display: none;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) input[type="checkbox"]:checked {color: black !important;opacity: 1;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) input[type="checkbox"]:checked + label {font-size: 1em;color: black !important;opacity: 1;font-weight: bold;}
ul:not(:first-of-type) li:first-of-type input + label {font-weight: bold;font-size: 1.4em;opacity: 1;display: inline !important;pointer-events: none;}
ul:not(:first-of-type) li:first-of-type input {display: none;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul, div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul + hr {display: none;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul:has(.joplin-checkbox input:checked), div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul:has(.joplin-checkbox input:checked) + hr {display: block;}
</style>

- [ ] Shopping mode
---
- [ ] **Fruits**
- [x] Apples
- [ ] Oranges
---
- [ ] **Vegetables**
- [x] Tomato
Cheaper in shop A than shop B
(only € 1.80)
![Lorem Picsum](upload://ydNdjgBgkxXiwOp5ZPCN0De7izB.jpeg)
- [x] Onions
---
- [ ] **Others**
- [x] Toilet Paper

Tested on Android

Improvement

Like I said before, one last improvement would be to add the ability to display an item's comments and pictures only on demand, and not all the time. Not sure that's doable in pure CSS though. If anybody has an idea...

1 Like

Now, after some more testing, I start to believe that's the Android app that has some kind of weird behavior. Indeed, in the desktop app checked items are by default transparent. According to you, it's the same in the iOS app. But not on the Android app, by default checked items are all bold.

As for a way to make all text non-transparent, @danielfomin96 could you please copy/paste the following code into a Joplin note, and tell me how it's rendered on iOS ?

I made 2 modifications :

  • I added some hidden <h3> headers, in order to build a table of content ([toc])
    [EDIT 4] There's a limitation : in shopping mode the TOC lists all categories, even the ones that don't have checked items. I don't think this issue can be solved by CSS.
  • And I added some style to the checked checkboxes, in order to display the item in bold, and the description in regular weight. For that I display the label in block mode. The result is as expected on the desktop app, but again on the Android app everything is bold, including the description. It appears I cannot style a label item on the Android app, that's probably a bug. Now, according to what you told me I expect it to render just fine on the iOS app (e.g., non-transparent and bold item, and regular item description). @danielfomin96 Could you please test it and tell me the result ?

Verbose CSS

<style>
input[type=checkbox]:checked {
  width: 17px;
  height: 17px;
  float: left;
}
input[type=checkbox]:checked + label {
  font-size: 1.1em;
  opacity: 1;
  display: block;
}
	
input[type=checkbox]:checked + label::first-line {
  font-weight: bold;
}

h3 {
  visibility: hidden;
  height: 0px;
  margin: 0px;
  padding: 0px:
}
/* Prep mode */
div:has(ul:first-of-type .joplin-checkbox:first-child input:not(:checked))
  input[type="checkbox"]:checked {
  width: 17px;
  height: 17px;
}
div:has(ul:first-of-type .joplin-checkbox:first-child input:not(:checked)) input[type="checkbox"]:checked + label {
  font-weight: bold;
  font-size: 1.1em;
  opacity: 1;
}

/* Shopping mode */
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  input[type="checkbox"]:not(:checked) {
  display: none;
}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  input[type="checkbox"]:not(:checked)
  + label {
  display: none;
}
	
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  input[type="checkbox"]:checked {
  color: black !important;
  opacity: 1;
}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  input[type="checkbox"]:checked
  + label {
  font-size: 1em;
  color: black !important;
  opacity: 1;
  font-weight: bold;
}

/* List Headings */
ul:not(:first-of-type) li:first-of-type input + label {
  font-weight: bold;
  font-size: 1.4em;
  opacity: 1;
  display: inline !important;
  pointer-events: none;
}
ul:not(:first-of-type) li:first-of-type input {
  display: none;
}

/* Hide Headings */
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul,
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul + hr {
  display: none;
}
	
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul:has(.joplin-checkbox input:checked),
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul:has(.joplin-checkbox input:checked) + hr {
  display: block;
}

nav.table-of-contents ul {
  display: block !important;
}
</style>
[toc]
- [x] Shopping mode
---
### Fruits
- [ ] **Fruits**
- [ ] Apples
- [x] Oranges
---
### Vegetables
- [ ] **Vegetables**
- [x] Tomato
Cheaper in shop A than shop B
(only € 1.80)
![Lorem Picsum](https://picsum.photos/200)
- [ ] Onions
---
### Others
- [ ] **Others**
- [x] Toilet Paper

[EDIT] : added some CSS in Hide Headings section in order to display the TOC in shopping mode also. I'm not sure that's such a good idea though, given that in shopping mode the TOC lists all categories, even those without checked items (see my previous remark, [EDIT 4]). Maybe it's better to not show the TOC at all in shopping mode. Just delete nav.table-of-contents ul {display: block !important;} in that case.

[EDIT 2] : minimized CSS

<style>
input[type=checkbox]:checked {width: 17px;height: 17px;float: left;}
input[type=checkbox]:checked + label {font-size: 1.1em;opacity: 1;display: block;}
input[type=checkbox]:checked + label::first-line {font-weight: bold;}
h3 {visibility: hidden;height: 0px;margin: 0px;padding: 0px:}
div:has(ul:first-of-type .joplin-checkbox:first-child input:not(:checked)) input[type="checkbox"]:checked {width: 17px;height: 17px;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:not(:checked)) input[type="checkbox"]:checked + label {font-weight: bold;font-size: 1.1em;opacity: 1;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) input[type="checkbox"]:not(:checked) {display: none;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) input[type="checkbox"]:not(:checked) + label {display: none;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) input[type="checkbox"]:checked {color: black !important;opacity: 1;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) input[type="checkbox"]:checked + label {font-size: 1em;color: black !important;opacity: 1;font-weight: bold;}
ul:not(:first-of-type) li:first-of-type input + label {font-weight: bold;font-size: 1.4em;opacity: 1;display: inline !important;pointer-events: none;}
ul:not(:first-of-type) li:first-of-type input {display: none;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul, div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul + hr {display: none;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul:has(.joplin-checkbox input:checked), div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul:has(.joplin-checkbox input:checked) + hr {display: block;}
nav.table-of-contents ul {display: block !important;}
</style>
[toc]
- [x] Shopping mode
---
### Fruits
- [ ] **Fruits**
- [x] Apples
- [x] Oranges
---
### Vegetables
- [ ] **Vegetables**
- [ ] Tomato
Cheaper in shop A than shop B
(only € 1.80)
![Lorem Picsum](https://picsum.photos/200)
- [ ] Onions
---
### Others
- [ ] **Others**
- [x] Toilet Paper

[EDIT 3] : added margin and padding to h3 styling

1 Like

Here are the two cases:



I did not look at the CSS but at least in shopping mode I would say an expected result can be seen. In the other mode the description also bold.

Deleted previous post because I found the cause, there were a few extra font-weight: bold; in the CSS. The following code works fine on Android, both in prep mode and shopping mode :
(minimized CSS version below)

<style>
input[type=checkbox]:checked {
  width: 17px;
  height: 17px;
  float: left;
}
input[type=checkbox]:checked + label {
  font-size: 1em;
  opacity: 1;
  display: block;
}
	
input[type=checkbox]:checked + label::first-line {
  font-weight: bold;
}

h3 {
  visibility: hidden;
  height: 0px;
  margin: 0px;
  padding: 0px:
}
/* Prep mode */
div:has(ul:first-of-type .joplin-checkbox:first-child input:not(:checked))
  input[type="checkbox"]:checked {
  width: 17px;
  height: 17px;
}
div:has(ul:first-of-type .joplin-checkbox:first-child input:not(:checked)) input[type="checkbox"]:checked + label {
  font-size: 1em;
  opacity: 1;
}

/* Shopping mode */
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  input[type="checkbox"]:not(:checked) {
  display: none;
}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  input[type="checkbox"]:not(:checked)
  + label {
  display: none;
}
	
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  input[type="checkbox"]:checked {
  color: black !important;
  opacity: 1;
}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked)
  input[type="checkbox"]:checked
  + label {
  font-size: 1em;
  color: black !important;
  opacity: 1;
}

/* List Headings */
ul:not(:first-of-type) li:first-of-type input + label {
  font-weight: bold;
  font-size: 1.4em;
  opacity: 1;
  display: inline !important;
  pointer-events: none;
}
ul:not(:first-of-type) li:first-of-type input {
  display: none;
}

/* Hide Headings */
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul,
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul + hr {
  display: none;
}
	
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul:has(.joplin-checkbox input:checked),
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul:has(.joplin-checkbox input:checked) + hr {
  display: block;
}

nav.table-of-contents ul {
  display: block !important;
}
	
</style>
[toc]
- [ ] Shopping mode
---
### Fruits
- [ ] **Fruits**
- [ ] Apples
- [ ] Oranges
---
### Vegetables
- [ ] **Vegetables**
- [ ] Tomato
Cheaper in shop A than shop B
(only € 1.80)
![Lorem Picsum](https://picsum.photos/200)
- [ ] Onions
---
### Others
- [ ] **Others**
- [ ] Toilet Paper

Screenshots (Android) :

Minimized CSS :

<style>
input[type=checkbox]:checked {width: 17px;height: 17px;float: left;}
input[type=checkbox]:checked + label {font-size: 1em;opacity: 1;display: block;}
input[type=checkbox]:checked + label::first-line {font-weight: bold;}
h3 {visibility: hidden;height: 0px;margin: 0px;padding: 0px:}
div:has(ul:first-of-type .joplin-checkbox:first-child input:not(:checked)) input[type="checkbox"]:checked {width: 17px;height: 17px;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:not(:checked)) input[type="checkbox"]:checked + label {font-size: 1em;opacity: 1;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) input[type="checkbox"]:not(:checked) {display: none;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) input[type="checkbox"]:not(:checked) + label {display: none;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) input[type="checkbox"]:checked {color: black !important;opacity: 1;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) input[type="checkbox"]:checked + label {font-size: 1em;color: black !important;opacity: 1;}
ul:not(:first-of-type) li:first-of-type input + label {font-weight: bold;font-size: 1.4em;opacity: 1;display: inline !important;pointer-events: none;}
ul:not(:first-of-type) li:first-of-type input {display: none;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul, div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul + hr {display: none;}
div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul:has(.joplin-checkbox input:checked), div:has(ul:first-of-type .joplin-checkbox:first-child input:checked) ul:has(.joplin-checkbox input:checked) + hr {display: block;}
nav.table-of-contents ul {display: block !important;}
</style>
[toc]
- [x] Shopping mode
---
### Fruits
- [ ] **Fruits**
- [ ] Apples
- [x] Oranges
---
### Vegetables
- [ ] **Vegetables**
- [x] Tomato
Cheaper in shop A than shop B
(only € 1.80)
![Lorem Picsum](https://picsum.photos/200)
- [ ] Onions
---
### Others
- [ ] **Others**
- [ ] Toilet Paper

This is great, I was also going to suggest using some kind of toggle to show/hide the list, which is far more practical in the event you accidentally uncheck something while you're at the shop and it disappears , or you remember something when you're already away from the computer and want to add it to the list.

I assume this also means you can apply this ONLY to the notes you want to, not to every note on Joplin with checkboxes, which is more practical. (Moot points, I realise now you are adding the CSS specifically to this note.)

I use Joplin more for GTD type lists than notes. I already had used CSS to hide checked items from the preview pane, but now I no longer use the preview pane at all as I use the Rich Markdown plugin (btw, in that, images are just a link till you click or hover over them) and CSS tweaks to make the edit pane very practical to look at.

Now I have some ideas to look more into using a toggle and hiding completed items, even from the edit pane :slight_smile:

Update: Have now created a new thread CSS to hide completed checkbox items in the Editor (specifically using Rich Markdown) with an implementation to do this for the Editor Pane.

Of note, I used a toggle in the note title, rather than the note body, due to DOM elements being removed as you scroll. Likely the above checkbox hack for the shopping list could also run into a similar issue on desktop that I experienced when scrolling a longer list.

@chentao Backing to the shopping context - why could you not use the checkboxes in their default states, with an unchecked checkbox appearing on your page, and checked checkboxes being hidden? I.e. not inverting the logic of the checkbox.

This is how I had been doing my Joplin shopping list - a single list with all my usual items on it, and having unchecked the items that I needed to pick up. The missing step for me would have been a toggle to hide what was already completed.

I have to admit I am surprised how much interest this topic seems to stir up. Because in reality (the reality of my Android device's performance)

  • Joplin is too slow to start when needed for quick edits (as in a shopping list)
  • or Jopiln has to run in the background all the time as a service (which currently it does not do, and which would take a lot of ressources needed for other apps)
  • and even when it does run already, Joplin is by no means a slim, fast, reactive application.

I really believe a dedicated light, slim, simple app for your shopping list would be a much better solution.

This all said, Joplin is a great app and a great solution "for what it's designed for".
5 stars from my side anyway.

1 Like

That‘s all true, but there is one thing, that makes this Shopping List in Joplin better than anywhere else (from what I have seen so far) and it‘s mentioned in the original post here:

That is the key factor for me: being able to reduce the list on the relevant entries when I‘m at the shop.

1 Like

@semyl : Well, it all comes down to the use of a dual list (pantry list and shopping list, see my original post). What we did (with @danielfomin96's crucial idea of using the checkbox hack) is "merge" both list into one single notes. So, basically, not inverting the logic for the shopping list would invert the logic for the pantry list.

Right now, the pantry list has the right logic : all items are unchecked by default, and checking an item "adds it to the shopping list" (so to speak, because of course there's only one list). The shopping list then has an inverted logic : the items are all checked, and we uncheck them when we buy them.

We can indeed invert the whole logic : checking by default all items of the pantry list. Uncheck an item to put it in the shopping list. Then all items to buy are unchecked, and we check them when we buy them.

The shopping list would be more intuitive this way (check when you buy), but then the pantry list woud be counter intuitive (uncheck to add to the shopping list).

Both methods works, whatever you prefer.

To have both lists in an intuitive state, we'd need :

  • All items unchecked by default. Checking an item "adds it to the shopping list". Intuitive.
  • Then invert all checkbox states when entering Shopping mode, which would provide us with an intuitive shopping list (items to buy are unchecked by default).

But I don't know how to invert all checkbox states when entering Shopping mode. I don't think that can be done with CSS only.

Joplin is indeed slow and a dedicated simple app would be much faster, however using such an app means that you likely have to manage the shopping list on the phone only. I personally hate typing anything longer with a touch screen, so I do all my modifications on the desktop, and only use the phone to view the list and tick the items.

1 Like

Well, @Wimvan gave you some reasons. I listed some desired features in my first post, and not many apps fit the bill. In fact, I only found one (Out of Milk, see above), but it's limited to US and Canada. Thus why I dug into Joplin, and am more than happy with the result of this topic. Joplin indeed is way better than most of the dedicated shopping apps out there

That's exactly how I use it too.

I am a little bit surprised by this statement. On my phone (Galaxy S20 from 2 or 3 years ago) it takes less than a second to show up. Authenticate with fingerprint. Done. I have a few hundred notes, maybe you have more.

Only caveat is the sync. Especially while shopping, every modification triggers a sync operation. So each time you (un)check an item because you just put in in your shopping basket, it syncs. It would be great to be able to temporarily disable the sync. The next best thing I found was to check "Sync only over WiFi" while shopping, so it doesn't sync. But you have to not forget to switch it back. A visible switch on the main screen would be way better. I opened a separate topic for that.

As a side note : don't forget that Joplin now supports profiles, both on Desktop and mobile. Great feature. No need to sync notes that you know you are never going to use on your mobile, make a dedicated desktop-only profile for this. And even on the mobile, you can separate your notes into several profiles, depending on your needs. So if you have a lot of notes, try and think if it would be appropriate to split them into several profiles.

Galaxy S20 is still a top-tier device with a beefy CPU :wink:. If you try to use Joplin on mid-range or low-end hardware, it launches very slowly and the UI is laggy all the time.

1 Like