added content
This commit is contained in:
parent
bf2221b9a9
commit
9b91b02d90
79 changed files with 3053 additions and 284 deletions
111
src/content/fragments/en/acme-sh-tls-cert.md
Normal file
111
src/content/fragments/en/acme-sh-tls-cert.md
Normal file
|
@ -0,0 +1,111 @@
|
|||
---
|
||||
title: Strong TLS certificates with acme.sh
|
||||
subtitle: 384-bit of https
|
||||
lang: en
|
||||
slug: "acme-sh-tls-cert"
|
||||
createdAt: "2022-06-08T14:24:06.000Z"
|
||||
excerpt: Real cert have curves.
|
||||
tags: ["security"]
|
||||
---
|
||||
|
||||
## Disclaimer
|
||||
|
||||
I'm, in absolutely no regards, a security expert. I just fancy shiny new things of the interwebs.
|
||||
This is why I've switched my default TLS certificates to use elliptic curve cryptography (ECC) instead of RSA. Now I have a sweet 100/100 on [tls.imirhil.fr](https://tls.imirhil.fr/)
|
||||
|
||||
You can learn (far) more by reading [this topic](https://crypto.stackexchange.com/questions/1190/why-is-elliptic-curve-cryptography-not-widely-used-compared-to-rsa) and its linked resources.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Installing acme.sh
|
||||
|
||||
For automation and ease of use purposes, I'm using [acme.sh](https://github.com/acmesh-official/acme.sh)
|
||||
|
||||
```bash
|
||||
# for using standalone mode, you might have to install as sudo
|
||||
curl https://get.acme.sh | sh -s email=mail@domain.tld
|
||||
```
|
||||
|
||||
### Changing default authority
|
||||
|
||||
By default, acme.sh uses ZeroSSL to sign certificates. We need to change this to Let's Encrypt because according to acme.sh, they're the only ones offering ECC capabilities.
|
||||
|
||||
```bash
|
||||
acme.sh --set-default-ca --server letsencrypt
|
||||
```
|
||||
|
||||
## Using your DNS api
|
||||
|
||||
If available, the easiest way to issue a certificate is to use the DNS api of your DNS provider. acme.sh supports [a lot](https://github.com/acmesh-official/acme.sh/wiki/dnsapi) of DNS providers.
|
||||
|
||||
### Define an api key
|
||||
|
||||
Follow the [docs](https://github.com/acmesh-official/acme.sh/wiki/dnsapi) for your DNS provider, usually:
|
||||
|
||||
```bash
|
||||
export PROVIDER_Key="YOUR_API_KEY"
|
||||
```
|
||||
|
||||
### Issue the cert
|
||||
|
||||
```bash
|
||||
acme.sh --issue -d domain.tld --dns dns_provider --keylength ec-384
|
||||
```
|
||||
|
||||
## Using standalone mode
|
||||
|
||||
If you don't have access to the DNS provider, we can use the standalone mode to spin up a temporary web server that will handle all the verifications.
|
||||
|
||||
Port `80` must be free.
|
||||
|
||||
```bash
|
||||
acme.sh --issue --standalone -d domain.tld --keylength ec-384
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Multi domains standalone
|
||||
|
||||
```bash
|
||||
acme.sh --issue --standalone -d domain.tld -d www.domain.tld -d subdomain.domain.tld --keylength ec-384
|
||||
```
|
||||
|
||||
### Wildcard domain DNS
|
||||
|
||||
```bash
|
||||
acme.sh --issue -d domain.tld -d '*.domain.tld' --dns dns_provider --keylength ec-384
|
||||
```
|
||||
|
||||
## Next steps
|
||||
|
||||
The ECC certificate alone will not grant you a high/perfect score.
|
||||
|
||||
### TLS version
|
||||
|
||||
Limit TLS version to 1.2 and 1.3 (or just 1.3 as there is only a [5% compatibility gap](https://caniuse.com/?search=tls%201.) with 1.2).
|
||||
|
||||
### HSTS
|
||||
|
||||
Use the [strict transport security](https://scotthelme.co.uk/hsts-the-missing-link-in-tls/) header.
|
||||
|
||||
```
|
||||
Strict-Transport-Security: max-age=31536000; includeSubDomains
|
||||
```
|
||||
|
||||
### Cipher suite
|
||||
|
||||
Use recent and strong ciphers. This is where my knowledge hits its limit… I'm having a really hard time understanding what to use and why.
|
||||
|
||||
I've based my initial choices of ciphers on [this list](https://tls.imirhil.fr/ciphers), cross referencing it with (older?) [browser compatibility](https://tls.imirhil.fr/suite).
|
||||
|
||||
I then asked [Aeris](https://twitter.com/aeris22), the creator of [tls.imirhil.fr](https://tls.imirhil.fr), about it and he advised me to use the following:
|
||||
|
||||
```
|
||||
ECDHE+AES:ECDHE+CHACHA20
|
||||
```
|
||||
|
||||
In order to achieve a perfect score, we can be a little more restrictive with:
|
||||
|
||||
```
|
||||
ECDHE+AES256:ECDHE+CHACHA20
|
||||
```
|
63
src/content/fragments/en/array-vs-array.md
Normal file
63
src/content/fragments/en/array-vs-array.md
Normal file
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
title: Filter an array against another array
|
||||
subtitle: Array vs Array.
|
||||
lang: en
|
||||
slug: "array-vs-array"
|
||||
createdAt: "2022-06-08T14:24:06.000Z"
|
||||
excerpt: My peak javascript
|
||||
tags: ["nuxt.js"]
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
For a project, I had to come up with a way to filter an array of objects based on another array of strings.
|
||||
|
||||
In other words, I needed to include every object which `user` property was found in the second array.
|
||||
|
||||
Here is an example:
|
||||
|
||||
```javascript
|
||||
const skills = [
|
||||
{
|
||||
name: "Be awesome",
|
||||
user: "Jean",
|
||||
},
|
||||
{
|
||||
name: "Great jokes",
|
||||
user: "Jacques",
|
||||
},
|
||||
{
|
||||
name: "Heavy sleeper",
|
||||
user: "Jean",
|
||||
},
|
||||
{
|
||||
name: "Heavy sleeper",
|
||||
user: "Beatriz",
|
||||
},
|
||||
];
|
||||
|
||||
const selectedUsers = ["Jean", "Beatriz"];
|
||||
```
|
||||
|
||||
I thought it would be pretty easy but I guess I'm not familiar enough with javascript 😬
|
||||
|
||||
## My solution
|
||||
|
||||
After a bit of thinking, I came up with the following statement:
|
||||
|
||||
> I need to filter the skills based on which users are selected. So I need to loop over the `selectedUsers` array and filter the skills according to their `user` value.
|
||||
|
||||
After a bit more trials and errors, this is the code I ended up using in a `computed property`:
|
||||
|
||||
```javascript
|
||||
// index.vue
|
||||
const filteredSkills = selectedUsers.map((user) => {
|
||||
return skills.filter((skill) => {
|
||||
return skill.user === user;
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
I used `map()` in order to loop on the second array and used its string value to only include the corresponding skills with `filter()`.
|
||||
|
||||
I'm pretty sure there is a better way to do it though…
|
189
src/content/fragments/en/buttons.md
Normal file
189
src/content/fragments/en/buttons.md
Normal file
|
@ -0,0 +1,189 @@
|
|||
---
|
||||
title: Buttons hover
|
||||
subtitle: Simple, but nice.
|
||||
lang: en
|
||||
slug: "buttons"
|
||||
excerpt: Easy to grab and use hover effects.
|
||||
tags: ["CSS"]
|
||||
code: true
|
||||
createdAt: "2020-10-08T09:00:00.000Z"
|
||||
---
|
||||
|
||||
## General rules
|
||||
|
||||
All the buttons use these styles as a “reset”:
|
||||
|
||||
> Don't forget to prefix if necessary!
|
||||
|
||||
```css
|
||||
.btn {
|
||||
margin: 20px 0;
|
||||
padding: 12px 26px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
font-size: 20rem; /* 20px */
|
||||
line-height: 1.6;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
color: white;
|
||||
background-color: hotpink;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
```
|
||||
|
||||
## Add an icon
|
||||
|
||||
<button role="none" class="btn btn-icon">
|
||||
<span>Icon</span>
|
||||
</button>
|
||||
|
||||
```css
|
||||
.btn-icon {
|
||||
background-color: hotpink;
|
||||
}
|
||||
.btn-icon::before {
|
||||
content: url("~assets/svg/arrow-right-white.svg");
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
transform: translate(40px, -50%);
|
||||
transition: transform ease 0.3s;
|
||||
}
|
||||
.btn-icon:hover,
|
||||
.btn-icon:focus {
|
||||
background-color: darkorchid;
|
||||
}
|
||||
.btn-icon:hover::before,
|
||||
.btn-icon:focus::before {
|
||||
transform: translate(-10px, -50%);
|
||||
}
|
||||
.btn-icon > span {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.btn-icon:hover > span,
|
||||
.btn-icon:focus > span {
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
```
|
||||
|
||||
## Double shutter down
|
||||
|
||||
<button role="none" class="btn btn-rideau">
|
||||
<span>Shutter</span>
|
||||
</button>
|
||||
|
||||
```css
|
||||
.btn-rideau {
|
||||
border: 2px solid #10113a;
|
||||
color: #10113a;
|
||||
background-color: transparent;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
.btn-rideau:hover {
|
||||
color: white;
|
||||
}
|
||||
.btn-rideau::before {
|
||||
background: hotpink;
|
||||
}
|
||||
.btn-rideau::after {
|
||||
background: darkorchid;
|
||||
}
|
||||
.btn-rideau::before,
|
||||
.btn-rideau::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
bottom: 100%;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
transition: transform 0.3s;
|
||||
transition-timing-function: ease;
|
||||
transition-timing-function: cubic-bezier(0.75, 0, 0.125, 1);
|
||||
}
|
||||
.btn-rideau:hover::before,
|
||||
.btn-rideau:hover::after,
|
||||
.btn-rideau:focus::before,
|
||||
.btn-rideau:focus::after {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
.btn-rideau:hover::after,
|
||||
.btn-rideau:focus::after {
|
||||
transition-delay: 0.175s;
|
||||
}
|
||||
```
|
||||
|
||||
## Animated gradient
|
||||
|
||||
<button role="none" class="btn btn-gradient">
|
||||
<span>Gradient</span>
|
||||
</button>
|
||||
|
||||
```css
|
||||
.btn-gradient {
|
||||
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
|
||||
background-size: 400% 400%;
|
||||
background-position: 0% 50%;
|
||||
animation: GradientReverse 0.5s ease 1 normal forwards;
|
||||
}
|
||||
.btn-gradient:hover,
|
||||
.btn-gradient:focus {
|
||||
animation: Gradient 0.5s ease 1 normal forwards;
|
||||
}
|
||||
@keyframes Gradient {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
}
|
||||
@keyframes GradientReverse {
|
||||
0% {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Non destructive scale
|
||||
|
||||
<button role="none" class="btn btn-scale">
|
||||
<span>Scale</span>
|
||||
</button>
|
||||
|
||||
```css
|
||||
.btn-scale {
|
||||
overflow: visible;
|
||||
color: #10113a;
|
||||
background-color: transparent;
|
||||
}
|
||||
.btn-scale::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
border: 2px solid #10113a;
|
||||
border-radius: 2px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.btn-scale:hover::after,
|
||||
.btn-scale:focus::after {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
```
|
11
src/content/fragments/en/image-full.md
Normal file
11
src/content/fragments/en/image-full.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: Full width image
|
||||
subtitle: Translation in progress, stay tuned ;)
|
||||
lang: en
|
||||
slug: "image-full"
|
||||
createdAt: "2020-09-15T09:00:00.000Z"
|
||||
updatedAt: "2022-06-08T14:24:06.000Z"
|
||||
tags: ["CSS"]
|
||||
---
|
||||
|
||||
[Go back to available snippets](/en/snippets/)
|
139
src/content/fragments/en/nuxt-graphql-static.md
Normal file
139
src/content/fragments/en/nuxt-graphql-static.md
Normal file
|
@ -0,0 +1,139 @@
|
|||
---
|
||||
title: Static website and GraphQL queries with Nuxt.js
|
||||
subtitle: Graphql client is king.
|
||||
lang: en
|
||||
slug: "nuxt-graphql-static"
|
||||
createdAt: "2022-06-08T14:24:06.000Z"
|
||||
updatedAt: "2022-09-08T13:43:33.000Z"
|
||||
excerpt: When the most used gql module doesn't work…
|
||||
tags: ["nuxt.js"]
|
||||
---
|
||||
|
||||
## The problem
|
||||
|
||||
I encountered a nasty bug while using static generation with Nuxt and [nuxt apollo](https://github.com/nuxt-community/apollo-module "Dépôt github du module nuxt apollo (new tab)") client.
|
||||
I found [the issue](https://github.com/nuxt-community/apollo-module/issues/339 "Github issue on nuxt/apollo repository (new tab)") already reported on github.
|
||||
|
||||
It seems the module doesn't handle static generation correctly with `nuxt generate`.
|
||||
|
||||
I could find request to my local API url after the static generation. Moreover, it also seemed like `<nuxt-link>` navigation was broken.
|
||||
|
||||
## The solution 🙌
|
||||
|
||||
Fortunately, there is another Nuxt module that handles GraphQL requests!
|
||||
|
||||
[Nuxt graphql request to the rescue!](https://github.com/gomah/nuxt-graphql-request)
|
||||
|
||||
### The conf
|
||||
|
||||
```javascript
|
||||
// nuxt.config.js
|
||||
buildModules: [
|
||||
'nuxt-graphql-request',
|
||||
],
|
||||
graphql: {
|
||||
clients: {
|
||||
default: {
|
||||
endpoint: 'http://API_URL/graphql',
|
||||
options: {
|
||||
headers: {
|
||||
authorization: 'Bearer API_TOKEN',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
### The request
|
||||
|
||||
The best approach so far is to use `asyncData` in pages and `fetch` in components. Using `fetch` in pages does not work well at all with `nuxt generate`.
|
||||
|
||||
I also install the `graphql-tag` package (only in `devDependencies`) to be able to import directly `.gql` files.
|
||||
|
||||
Query example:
|
||||
|
||||
```graphql
|
||||
# homepage.gql
|
||||
query {
|
||||
homepage {
|
||||
title
|
||||
subtitle
|
||||
hero {
|
||||
id
|
||||
alt
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Inside a page
|
||||
|
||||
```javascript
|
||||
// index.vue
|
||||
<script>
|
||||
import homepageQuery from '~/graphql/queries/singles/homepage'
|
||||
|
||||
export default {
|
||||
async asyncData({ $graphql }) {
|
||||
const data = await $graphql.default.request(homepageQuery)
|
||||
return { data }
|
||||
},
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
#### Inside a component
|
||||
|
||||
It is safer to wait until `fetch` has received a response before displaying anything. You can use `$fetchState` to be sure ([documentation](https://nuxtjs.org/docs/2.x/components-glossary/pages-fetch "Documentation on the fetch hook (new tab)")).
|
||||
|
||||
```javascript
|
||||
// Header.vue
|
||||
<template>
|
||||
<header v-if="!$fetchState.pending">
|
||||
…
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import headerQuery from '~/graphql/queries/singles/header'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
data: {},
|
||||
}
|
||||
},
|
||||
async fetch() {
|
||||
try {
|
||||
const data = await this.$graphql.default.request(headerQuery)
|
||||
this.data = data
|
||||
} catch (error) {
|
||||
console.error(JSON.stringify(error, undefined, 2))
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
To pass options to the request, for example for a multilingual version with [nuxt/i18n](https://i18n.nuxtjs.org/) and/or a url parameter in a dynamic page:
|
||||
|
||||
```javascript
|
||||
// _slug.vue
|
||||
<script>
|
||||
import articleQuery from '~/graphql/queries/articles'
|
||||
|
||||
export default {
|
||||
async asyncData({ $graphql, app, params }) {
|
||||
const locale = app.i18n.localeProperties.iso
|
||||
const data = await $graphql.default.request(articleQuery, {
|
||||
code: locale,
|
||||
slug: params.slug,
|
||||
})
|
||||
return { data }
|
||||
},
|
||||
}
|
||||
</script>
|
||||
```
|
74
src/content/fragments/en/super-cookies.md
Normal file
74
src/content/fragments/en/super-cookies.md
Normal file
|
@ -0,0 +1,74 @@
|
|||
---
|
||||
title: The best cookies
|
||||
subtitle: Consentless biscuits.
|
||||
lang: en
|
||||
slug: "super-cookies"
|
||||
createdAt: "2022-06-08T14:24:06.000Z"
|
||||
excerpt: It's a real recipe, not a joke about annoying files.
|
||||
tags: ["food"]
|
||||
---
|
||||
|
||||
## Vegetalian version
|
||||
|
||||
### Ingredients
|
||||
|
||||
- 250 grams of flour
|
||||
- 70 grams of muscovado/vergeoise sugar (unrefined)
|
||||
- 20 grams of brown sugar
|
||||
- 1 pinch of salt
|
||||
- 1 tablespoon of baking powder
|
||||
- 3/4 of aquafaba from 400g of chickpea (chickpea water)
|
||||
- 125 grams of coconut oil
|
||||
- 2 teaspoons of maple syrup
|
||||
- 1 chocolate bar (black) cut in “ chunks ”
|
||||
|
||||
### Method
|
||||
|
||||
- if necessary, melt the coconut oil over low heat
|
||||
- meanwhile, mix all the dry ingredients
|
||||
- add all liquid ingredients (oil, aquafaba, maple syrup)
|
||||
- mix well
|
||||
- add the chocolate
|
||||
- mix well (fig. 1)
|
||||
|
||||
### Baking
|
||||
|
||||
Form balls with the obtained mixture on a baking sheet.
|
||||
Bake for 9 minutes at 210 degrees (Celsius) - fan setting (fig. 2).
|
||||
|
||||
## Non vegetalian version
|
||||
|
||||
### Ingredients
|
||||
|
||||
- 250 grams of flour
|
||||
- 70 grams of muscovado/vergeoise sugar (unrefined)
|
||||
- 20 grams of brown sugar
|
||||
- 1 pinch of salt
|
||||
- 1 table spoon of baking powder
|
||||
- 1 egg
|
||||
- 125 grams of melted butter
|
||||
- 2 teaspoons of honey
|
||||
- 1 chocolate bar (black) cut in “ chunks ”
|
||||
|
||||
### Method
|
||||
|
||||
- melt butter over low heat
|
||||
- meanwhile, mix all dry ingredients
|
||||
- add all liquid ingredients (melted butter, egg, honey)
|
||||
- mix well
|
||||
- add the chocolate
|
||||
- mix well (fig. 1)
|
||||
|
||||
### Baking
|
||||
|
||||
Form balls with the obtained mixture on a baking sheet.
|
||||
Bake for 9 minutes at 210 degrees (Celsius) - fan setting (fig. 2).
|
||||
|
||||
## Notes
|
||||
|
||||
Chocolate can be replaced by anything like nuts, raisins, legos... (don't eat legos be reasonable).
|
||||
|
||||
## Figures
|
||||
|
||||

|
||||

|
68
src/content/fragments/en/toulouse-fun.md
Normal file
68
src/content/fragments/en/toulouse-fun.md
Normal file
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
title: Toulouse yourself
|
||||
subtitle: Only the bestest
|
||||
lang: en
|
||||
slug: "toulouse-fun"
|
||||
excerpt: Gonna have to trust me on this ¯\_(ツ)_/¯
|
||||
tags: ["lifestyle"]
|
||||
createdAt: "2022-06-22T15:34:45.000Z"
|
||||
---
|
||||
|
||||
Here is my list of great places to go to when in Toulouse. There are of course a lot of other great places to go to, these are just my all time favourite.
|
||||
**It's always a good idea to make a reservation!**
|
||||
|
||||
## Restaurants
|
||||
|
||||
### French cuisine
|
||||
|
||||
- [Solides](https://www.solides.fr/) — semi-gastronomic
|
||||
- [Les impulsifs](https://les-impulsifs-toulouse.eatbu.com/?lang=en) — semi-gastronomic
|
||||
- [Sixta](https://sixta-toulouse.fr/) — vege/vegan/gluten friendly
|
||||
- [Attila](https://attila.site-solocal.com/) — great **fish restaurant,** just above the Victor Hugo market
|
||||
- [Chez Emile](https://www.restaurant-emile.com/) - best [cassoulet](https://en.wikipedia.org/wiki/Cassoulet) in town (or so I'm told)
|
||||
|
||||
### Korean
|
||||
|
||||
- [Le ptit Louis](https://leptitlouis.fr/) — best of its kind, **only for lunch**
|
||||
- [Kongbap](https://kong-bap.com/) — street-food like
|
||||
|
||||
### Japanese
|
||||
|
||||
- [Iori](https://www.iori.fr/) — best ramen
|
||||
- [Japoyaki](https://www.qwant.com/maps/place/osm:node:2447719414@Japoyaki#map=16.50/43.6061725/1.4473402) — best sushi/sashimis
|
||||
|
||||
## Drinks and snacks
|
||||
|
||||
### Bars
|
||||
|
||||
- [Le Bièrographe](https://www.qwant.com/maps/place/osm:node:1532531236@Le_Bi%C3%A8rographe#map=16.50/43.5986892/1.4428327) — all time favourite, **check out the typical toulouse basement**
|
||||
- [A Taula](https://www.facebook.com/ataulatolosa/) — all time favourite in summer, **amazing terrace,** also: great tapas
|
||||
- [The Hopscotch Pub](https://www.qwant.com/maps/place/osm:node:5592180378@The_Hopscotch#map=18.14/43.6006796/1.4431385) — good beers, good whiskies, good cocktials, good food
|
||||
|
||||
### Cafés & tea rooms
|
||||
|
||||
- [Bapz](https://www.bapz.fr/contact) — Excellent pastries / hot beverages
|
||||
- [Ô thé divin](https://fr-fr.facebook.com/%C3%94-Th%C3%A9-Divin-245018828864405/) — nice lunch options
|
||||
- [Les Rêveries d’Hercule](https://www.lesreveriesdhercule.com/) — Pottery Café, relaxing activity (ceramics need to be baked, there'll be a delay to get them back)
|
||||
- Bonus: the best bakeries in [Carmes](https://www.qwant.com/maps/place/osm:node:450165912@Boulangerie_des_Carmes#map=16.37/43.6022638/1.4439872) and [Saint-Aubin](https://www.qwant.com/maps/place/osm:node:456844404@La_P%C3%A9trie#map=18.84/43.6045088/1.4544694) — croissants, baguettes **traditions**, chocolatines and more
|
||||
|
||||
### Wine shops
|
||||
|
||||
- [l'envie du sud](https://www.qwant.com/maps/place/osm:node:3861692629@LEnvie#map=19.57/43.5978194/1.4443930) — amazing selection of wines and other spirits, excellent advice from the staff
|
||||
- [enoteca](https://www.qwant.com/maps/place/osm:node:3751077562@Enoteca_31#map=16.50/43.6045636/1.4531952) — smaller selection but more beers and great advice from the staff as well
|
||||
|
||||
## Other delights
|
||||
|
||||
### Ice creams
|
||||
|
||||
- [Forno gusto](https://www.qwant.com/maps/place/osm:node:2462248749@Caf%C3%A9t%C3%A9ria_Gelateria#map=20.00/43.6027626/1.4421450) — only in the summer
|
||||
- [Moustache](https://www.qwant.com/maps/place/osm:node:2165543146@Glaces_Moustache#map=16.50/43.6036985/1.4350540) — lots of flavours
|
||||
|
||||
### Cheese shop
|
||||
|
||||
- [Xavier](https://xavier.fr/) — one of the **best of the country**, two shops in Toulouse, including one at Victor Hugo market
|
||||
- [Massembea](https://www.qwant.com/maps/place/osm:node:6164095797@Massembea#map=16.50/43.6043930/1.4539490) — a serious contender in Toulouse (weird hours)
|
||||
|
||||
### Delicatessen
|
||||
|
||||
- [Café Bacquié](http://cafe-bacquie.com/) — stock up on fine foods, coffees, teas and other great quality products
|
Loading…
Add table
Add a link
Reference in a new issue